forked from pb33f/libopenapi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdocument.go
146 lines (130 loc) · 5.79 KB
/
document.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
// Package libopenapi is a library containing tools for reading and in and manipulating Swagger (OpenAPI 2) and OpenAPI 3+
// specifications into strongly typed documents. These documents have two APIs, a high level (porcelain) and a
// low level (plumbing).
//
// Every single type has a 'GoLow()' method that drops down from the high API to the low API. Once in the low API,
// the entire original document data is available, including all comments, line and column numbers for keys and values.
//
// There are two steps to creating a using Document. First, create a new Document using the NewDocument() method
// and pass in a specification []byte array that contains the OpenAPI Specification. It doesn't matter if YAML or JSON
// are used.
package libopenapi
import (
"fmt"
"github.com/pb33f/libopenapi/datamodel"
v2high "github.com/pb33f/libopenapi/datamodel/high/v2"
v3high "github.com/pb33f/libopenapi/datamodel/high/v3"
v2low "github.com/pb33f/libopenapi/datamodel/low/v2"
v3low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
// Document Represents an OpenAPI specification that can then be rendered into a model or serialized back into
// a string document after being manipulated.
type Document interface {
// GetVersion will return the exact version of the OpenAPI specification set for the document.
GetVersion() string
// GetSpecInfo will return the *datamodel.SpecInfo instance that contains all specification information.
GetSpecInfo() *datamodel.SpecInfo
// BuildV2Model will build out a Swagger (version 2) model from the specification used to create the document
// If there are any issues, then no model will be returned, instead a slice of errors will explain all the
// problems that occurred. This method will only support version 2 specifications and will throw an error for
// any other types.
BuildV2Model() (*DocumentModel[v2high.Swagger], []error)
// BuildV3Model will build out an OpenAPI (version 3+) model from the specification used to create the document
// If there are any issues, then no model will be returned, instead a slice of errors will explain all the
// problems that occurred. This method will only support version 3 specifications and will throw an error for
// any other types.
BuildV3Model() (*DocumentModel[v3high.Document], []error)
// Serialize will re-render a Document back into a []byte slice. If any modifications have been made to the
// underlying data model using low level APIs, then those changes will be reflected in the serialized output.
//
// It's important to know that this should not be used if the resolver has been used on a specification to
// for anything other than checking for circular references. If the resolver is used to resolve the spec, then this
// method may spin out forever if the specification backing the model has circular references.
Serialize() ([]byte, error)
}
type document struct {
version string
info *datamodel.SpecInfo
}
// DocumentModel represents either a Swagger document (version 2) or an OpenAPI document (version 3) that is
// built from a parent Document.
type DocumentModel[T v2high.Swagger | v3high.Document] struct {
Model T
}
// NewDocument will create a new OpenAPI instance from an OpenAPI specification []byte array. If anything goes
// wrong when parsing, reading or processing the OpenAPI specification, there will be no document returned, instead
// a slice of errors will be returned that explain everything that failed.
//
// After creating a Document, the option to build a model becomes available, in either V2 or V3 flavors. The models
// are about 70% different between Swagger and OpenAPI 3, which is why two different models are available.
func NewDocument(specByteArray []byte) (Document, error) {
info, err := datamodel.ExtractSpecInfo(specByteArray)
if err != nil {
return nil, err
}
d := new(document)
d.version = info.Version
d.info = info
return d, nil
}
func (d *document) GetVersion() string {
return d.version
}
func (d *document) GetSpecInfo() *datamodel.SpecInfo {
return d.info
}
func (d *document) Serialize() ([]byte, error) {
if d.info == nil {
return nil, fmt.Errorf("unable to serialize, document has not yet been initialized")
}
if d.info.SpecFileType == datamodel.YAMLFileType {
return yaml.Marshal(d.info.RootNode)
} else {
yamlData, _ := yaml.Marshal(d.info.RootNode)
return utils.ConvertYAMLtoJSON(yamlData)
}
}
func (d *document) BuildV2Model() (*DocumentModel[v2high.Swagger], []error) {
var errors []error
if d.info == nil {
errors = append(errors, fmt.Errorf("unable to build swagger document, no specification has been loaded"))
return nil, errors
}
if d.info.SpecFormat != datamodel.OAS2 {
errors = append(errors, fmt.Errorf("unable to build swagger document, "+
"supplied spec is a different version (%v). Try 'BuildV3Model()'", d.info.SpecFormat))
return nil, errors
}
lowDoc, err := v2low.CreateDocument(d.info)
if err != nil {
return nil, err
}
highDoc := v2high.NewSwaggerDocument(lowDoc)
return &DocumentModel[v2high.Swagger]{
Model: *highDoc,
}, nil
}
func (d *document) BuildV3Model() (*DocumentModel[v3high.Document], []error) {
var errors []error
if d.info == nil {
errors = append(errors, fmt.Errorf("unable to build document, no specification has been loaded"))
return nil, errors
}
if d.info.SpecFormat != datamodel.OAS3 {
errors = append(errors, fmt.Errorf("unable to build openapi document, "+
"supplied spec is a different version (%v). Try 'BuildV2Model()'", d.info.SpecFormat))
return nil, errors
}
lowDoc, err := v3low.CreateDocument(d.info)
if err != nil {
return nil, err
}
highDoc := v3high.NewDocument(lowDoc)
return &DocumentModel[v3high.Document]{
Model: *highDoc,
}, nil
}