Skip to content

Commit 611ad34

Browse files
committed
Initial commit.
1 parent 5d1a973 commit 611ad34

31 files changed

+24239
-0
lines changed

CONTRIBUTORS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
go-raml Contributor List (ordered by date):
2+
3+
Alon Diamant <diamant.alon@gmail.com>
4+
Dvir Volk [https://github.com/dvirsky]
5+
6+
If anyone is missing, please message me.

LICENSE

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
Copyright 2014 DoAT. All rights reserved.
2+
3+
Redistribution and use in source and binary forms, with or without modification,
4+
are permitted provided that the following conditions are met:
5+
6+
1. Redistributions of source code must retain the above copyright notice,
7+
this list of conditions and the following disclaimer.
8+
9+
2. Redistributions in binary form must reproduce the above copyright notice,
10+
this list of conditions and the following disclaimer in the documentation and/or
11+
other materials provided with the distribution.
12+
13+
THIS SOFTWARE IS PROVIDED “AS IS” WITHOUT ANY WARRANTIES WHATSOEVER.
14+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15+
THE IMPLIED WARRANTIES OF NON INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A
16+
PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL DoAT OR CONTRIBUTORS
17+
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
22+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23+
24+
The views and conclusions contained in the software and documentation are those of
25+
the authors and should not be interpreted as representing official policies,
26+
either expressed or implied, of DoAT.

README.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,92 @@ raml
22
====
33

44
An implementation of a RAML parser for Go. Compliant with RAML 0.8.
5+
6+
Introduction
7+
============
8+
9+
RAML is a YAML-based language that describes RESTful APIs. Together with the
10+
YAML specification, this specification provides all the information necessary
11+
to describe RESTful APIs; to create API client-code and API server-code
12+
generators; and to create API user documentation from RAML API definitions.
13+
14+
The **_raml_** package enables Go programs to parse RAML files and valid RAML API
15+
definitions. It was originally developed within [EverythingMe](https://www.everything.me).
16+
17+
Status
18+
------
19+
20+
The **_raml_** package is currently immature and does not offering any kind of API
21+
stability guarantees.
22+
23+
Installation
24+
============
25+
26+
The yaml package may be installed by running:
27+
28+
$ go get gopkg.in/raml.v0
29+
30+
Opening that same URL in a browser will present a nice introductory page
31+
containing links to the documentation, source code, and all versions available
32+
for the given package.
33+
34+
The actual implementation of the package is in GitHub:
35+
36+
https://github.com/go-raml/raml
37+
38+
Contributing to development
39+
---------------------------
40+
41+
Typical installation process for developing purposes:
42+
43+
$ git clone git@github.com:go-raml/raml.git
44+
$ cd raml
45+
$ go build
46+
$ go install
47+
$ go test
48+
49+
Usage
50+
=====
51+
52+
Usage is very simple:
53+
54+
package main
55+
56+
import (
57+
"fmt"
58+
raml "gopkg.in/raml.v0"
59+
"github.com/kr/pretty"
60+
)
61+
62+
func main() {
63+
64+
fileName := "./samples/congo/api.raml"
65+
66+
if apiDefinition, err := ParseFile(fileName); err != nil {
67+
fmt.Printf("Failed parsing RAML file %s:\n %s", fileName, err.Error())
68+
} else {
69+
fmt.Printf("Successfully parsed RAML file %s!\n\n", fileName)
70+
pretty.Printf(apiDefinition)
71+
}
72+
}
73+
74+
Getting help
75+
============
76+
77+
* Look up the [RAML 0.8](http://raml.org/spec.html) spec if in doubt
78+
* Contact Alon, the maintainer directly: diamant.alon@gmail.com
79+
80+
Roadmap
81+
=======
82+
83+
TBD.
84+
85+
Reporting Bugs and Contributing Code
86+
====================================
87+
88+
* Want to report a bug or request a feature? Please open [an issue](https://github.com/go-raml/raml/issues/new).
89+
* Want to contribute to **_raml_**? Fork the project and make a pull request. Cool cool cool.
90+
91+
## License
92+
93+
See [LICENSE](https://github.com/go-raml/raml/blob/master/LICENSE) file.

parser.go

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
// Copyright 2014 DoAT. All rights reserved.
2+
//
3+
// Redistribution and use in source and binary forms, with or without modification,
4+
// are permitted provided that the following conditions are met:
5+
//
6+
// 1. Redistributions of source code must retain the above copyright notice,
7+
// this list of conditions and the following disclaimer.
8+
//
9+
// 2. Redistributions in binary form must reproduce the above copyright notice,
10+
// this list of conditions and the following disclaimer in the documentation and/or
11+
// other materials provided with the distribution.
12+
//
13+
// THIS SOFTWARE IS PROVIDED “AS IS” WITHOUT ANY WARRANTIES WHATSOEVER.
14+
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15+
// THE IMPLIED WARRANTIES OF NON INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A
16+
// PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL DoAT OR CONTRIBUTORS
17+
// BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18+
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19+
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20+
// // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21+
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
22+
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23+
//
24+
// The views and conclusions contained in the software and documentation are those of
25+
// the authors and should not be interpreted as representing official policies,
26+
// either expressed or implied, of DoAT.
27+
28+
// This package contains the parser, validator and types that implement the
29+
// RAML specification, as documented here:
30+
// http://raml.org/spec.html
31+
package raml
32+
33+
import (
34+
"bufio"
35+
"bytes"
36+
"errors"
37+
"fmt"
38+
"io"
39+
"io/ioutil"
40+
"path/filepath"
41+
"strings"
42+
43+
yaml "github.com/advance512/yaml"
44+
"github.com/kr/pretty"
45+
)
46+
47+
// A RamlError is returned by the ParseFile function when RAML or YAML problems
48+
// are encountered when parsing the RAML document.
49+
// When this error is returned, the value is still parsed partially.
50+
type RamlError struct {
51+
Errors []string
52+
}
53+
54+
// Parse a RAML file. Returns a raml.APIDefinition value or an error if
55+
// everything is something went wrong.
56+
func ParseFile(filePath string) (*APIDefinition, error) {
57+
58+
// Get the working directory
59+
workingDirectory, fileName := filepath.Split(filePath)
60+
61+
// Read original file contents into a byte array
62+
mainFileBytes, err := readFileContents(workingDirectory, fileName)
63+
64+
if err != nil {
65+
return nil, err
66+
}
67+
68+
// Get the contents of the main file
69+
mainFileBuffer := bytes.NewBuffer(mainFileBytes)
70+
71+
// Verify the YAML version
72+
var ramlVersion string
73+
if firstLine, err := mainFileBuffer.ReadString('\n'); err != nil {
74+
return nil, fmt.Errorf("Problem reading RAML file (Error: %s)", err.Error())
75+
} else {
76+
77+
// We read some data...
78+
if len(firstLine) >= 10 {
79+
ramlVersion = firstLine[:10]
80+
}
81+
82+
// TODO: Make this smart. We probably won't support multiple RAML
83+
// versions in the same package - we'll have different branches
84+
// for different versions. This one is hard-coded to 0.8.
85+
// Still, would be good to think about this.
86+
if ramlVersion != "#%RAML 0.8" {
87+
return nil, errors.New("Input file is not a RAML 0.8 file. Make " +
88+
"sure the file starts with #%RAML 0.8")
89+
}
90+
}
91+
92+
// Pre-process the original file, following !include directive
93+
preprocessedContentsBytes, err :=
94+
preProcess(mainFileBuffer, workingDirectory)
95+
96+
if err != nil {
97+
return nil,
98+
fmt.Errorf("Error preprocessing RAML file (Error: %s)", err.Error())
99+
}
100+
101+
pretty.Println(string(preprocessedContentsBytes))
102+
103+
// Unmarshal into an APIDefinition value
104+
apiDefinition := new(APIDefinition)
105+
apiDefinition.RAMLVersion = ramlVersion
106+
107+
// Go!
108+
err = yaml.Unmarshal(preprocessedContentsBytes, apiDefinition)
109+
110+
// Any errors?
111+
if err != nil {
112+
113+
return nil, fmt.Errorf("Problems parsing RAML:\n %s", err.Error())
114+
}
115+
116+
// Good.
117+
return apiDefinition, nil
118+
119+
}
120+
121+
// Reads the contents of a file, returns a bytes buffer
122+
func readFileContents(workingDirectory string, fileName string) ([]byte, error) {
123+
124+
filePath := filepath.Join(workingDirectory, fileName)
125+
126+
if fileName == "" {
127+
return nil, fmt.Errorf("File name cannot be nil: %s", filePath)
128+
}
129+
130+
// Read the file
131+
fileContentsArray, err := ioutil.ReadFile(filePath)
132+
if err != nil {
133+
return nil,
134+
fmt.Errorf("Could not read file %s (Error: %s)",
135+
filePath, err.Error())
136+
}
137+
138+
return fileContentsArray, nil
139+
}
140+
141+
// preProcess acts as a preprocessor for a RAML document in YAML format,
142+
// including files referenced via !include. It returns a pre-processed document.
143+
func preProcess(originalContents io.Reader, workingDirectory string) ([]byte, error) {
144+
145+
// NOTE: Since YAML doesn't support !include directives, and since go-yaml
146+
// does NOT play nice with !include tags, this has to be done like this.
147+
// I am considering modifying go-yaml to add custom handlers for specific
148+
// tags, to add support for !include, but for now - this method is
149+
// GoodEnough(TM) and since it will only happen once, I am not prematurely
150+
// optimizing it.
151+
152+
var preprocessedContents bytes.Buffer
153+
154+
// Go over each line, looking for !include tags
155+
scanner := bufio.NewScanner(originalContents)
156+
var line string
157+
158+
// Scan the file until we reach EOF or error out
159+
for scanner.Scan() {
160+
line = scanner.Text()
161+
162+
// Did we find an !include directive to handle?
163+
if idx := strings.Index(line, "!include"); idx != -1 {
164+
165+
// TODO: Do this better
166+
includeLength := len("!include ")
167+
168+
includedFile := line[idx+includeLength:]
169+
170+
preprocessedContents.Write([]byte(line[:idx]))
171+
172+
// Get the included file contents
173+
includedContents, err :=
174+
readFileContents(workingDirectory, includedFile)
175+
176+
if err != nil {
177+
return nil,
178+
fmt.Errorf("Error including file %s:\n %s",
179+
includedFile, err.Error())
180+
}
181+
182+
// TODO: Check that you only insert .yaml, .raml, .txt and .md files
183+
// In case of .raml or .yaml, remove the comments
184+
// In case of other files, Base64 them first.
185+
186+
// TODO: Better, step by step checks .. though prolly it'll panic
187+
// Write text files in the same indentation as the first line
188+
internalScanner :=
189+
bufio.NewScanner(bytes.NewBuffer(includedContents))
190+
191+
// Indent by this much
192+
firstLine := true
193+
indentationString := ""
194+
195+
// Go over each line, write it
196+
for internalScanner.Scan() {
197+
internalLine := internalScanner.Text()
198+
199+
preprocessedContents.WriteString(indentationString)
200+
if firstLine {
201+
indentationString = strings.Repeat(" ", idx)
202+
firstLine = false
203+
}
204+
205+
preprocessedContents.WriteString(internalLine)
206+
preprocessedContents.WriteByte('\n')
207+
}
208+
209+
} else {
210+
211+
// No, just a simple line.. write it
212+
preprocessedContents.WriteString(line)
213+
preprocessedContents.WriteByte('\n')
214+
}
215+
}
216+
217+
// Any errors encountered?
218+
if err := scanner.Err(); err != nil {
219+
return nil, fmt.Errorf("Error reading YAML file: %s", err.Error())
220+
}
221+
222+
// Return the preprocessed contents
223+
return preprocessedContents.Bytes(), nil
224+
}

0 commit comments

Comments
 (0)