diff --git a/internal/backstage/backstage_test.go b/internal/backstage/backstage_test.go index 9e899672..0c7afcd2 100644 --- a/internal/backstage/backstage_test.go +++ b/internal/backstage/backstage_test.go @@ -132,6 +132,7 @@ func TestLoadVersionsNoApis(t *testing.T) { - Registry_2023-06-01_experimental - Registry_2023-06-02_experimental - Registry_2023-06-03_experimental + - Registry_2024-10-15_ga --- `[1:]+string(vervetAPIs)) } @@ -164,6 +165,7 @@ func TestLoadVersionsSomeApis(t *testing.T) { - Registry_2023-06-01_experimental - Registry_2023-06-02_experimental - Registry_2023-06-03_experimental + - Registry_2024-10-15_ga - someOtherApi --- `[1:]+string(vervetAPIs)) @@ -193,6 +195,7 @@ func TestDoesNotOutputStabilitiesAfterPivotDate(t *testing.T) { - Registry_2023-06-01 - Registry_2023-06-02 - Registry_2023-06-03 + - Registry_2024-10-15 --- `[1:]+string(vervetAPIs)) } diff --git a/internal/compiler/compiler_test.go b/internal/compiler/compiler_test.go index 4deb16cd..2dfdc534 100644 --- a/internal/compiler/compiler_test.go +++ b/internal/compiler/compiler_test.go @@ -8,6 +8,7 @@ import ( "path/filepath" "testing" "text/template" + "time" qt "github.com/frankban/quicktest" @@ -157,7 +158,10 @@ func TestCompilerSmokePaths(t *testing.T) { } func assertOutputsEqual(c *qt.C, refDir, testDir string) { - err := fs.WalkDir(os.DirFS(refDir), ".", func(path string, d fs.DirEntry, err error) error { + pivotDate, err := time.Parse("2006-01-02", "2024-10-15") + c.Assert(err, qt.IsNil) + + err = fs.WalkDir(os.DirFS(refDir), ".", func(path string, d fs.DirEntry, err error) error { c.Assert(err, qt.IsNil) if d.IsDir() { return nil @@ -166,6 +170,14 @@ func assertOutputsEqual(c *qt.C, refDir, testDir string) { // only comparing compiled specs here return nil } + specDate, err := time.Parse("2006-01-02", path[:10]) + c.Assert(err, qt.IsNil) + if !specDate.Before(pivotDate) { + // After pivot date we exclusively use simplebuild so don't emit + // specs from this pipeline + return nil + } + outputFile, err := os.ReadFile(filepath.Join(testDir, path)) c.Assert(err, qt.IsNil) refFile, err := os.ReadFile(filepath.Join(refDir, path)) diff --git a/internal/simplebuild/build.go b/internal/simplebuild/build.go index c694c8b6..0d94bc89 100644 --- a/internal/simplebuild/build.go +++ b/internal/simplebuild/build.go @@ -97,7 +97,7 @@ func Build( return err } - err = writer.Write(doc) + err = writer.Write(ctx, doc) if err != nil { return err } @@ -151,7 +151,14 @@ func (ops Operations) Build(startVersion vervet.Version) DocSet { output := make(DocSet, len(versionDates)) for idx, versionDate := range versionDates { output[idx] = VersionedDoc{ - Doc: &openapi3.T{}, + Doc: &openapi3.T{ + OpenAPI: "3.0.3", + Info: &openapi3.Info{ + Title: "Snyk API", + Version: "1.0.0", + }, + Paths: openapi3.NewPaths(), + }, VersionDate: versionDate, } for path, spec := range filteredOps { diff --git a/internal/simplebuild/output.go b/internal/simplebuild/output.go index e709d206..eb428b33 100644 --- a/internal/simplebuild/output.go +++ b/internal/simplebuild/output.go @@ -1,6 +1,7 @@ package simplebuild import ( + "context" "fmt" "io/fs" "os" @@ -59,13 +60,18 @@ func NewWriter(cfg config.Output, appendOutputFiles bool) (*DocWriter, error) { // Write writes compiled specs to a single directory in YAML and JSON formats. // Call Finalize after to populate other directories. -func (out *DocWriter) Write(doc VersionedDoc) error { +func (out *DocWriter) Write(ctx context.Context, doc VersionedDoc) error { + err := doc.Doc.Validate(ctx) + if err != nil { + return fmt.Errorf("invalid compiled document: %w", err) + } + // We write to the first directory then copy the entire directory // afterwards dir := out.paths[0] versionDir := path.Join(dir, doc.VersionDate.Format(time.DateOnly)) - err := os.MkdirAll(versionDir, 0755) + err = os.MkdirAll(versionDir, 0755) if err != nil { return fmt.Errorf("make output directory: %w", err) } diff --git a/internal/simplebuild/output_test.go b/internal/simplebuild/output_test.go index 04d719d5..18135256 100644 --- a/internal/simplebuild/output_test.go +++ b/internal/simplebuild/output_test.go @@ -1,6 +1,7 @@ package simplebuild import ( + "context" "os" "path" "path/filepath" @@ -123,6 +124,7 @@ func TestDocSet_WriteOutputs(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() if tt.setup != nil { tt.setup(t, tt.args) } @@ -130,7 +132,7 @@ func TestDocSet_WriteOutputs(t *testing.T) { writer, err := NewWriter(tt.args.cfg, tt.args.appendOutputFiles) c.Assert(err, qt.IsNil) for _, doc := range tt.docs { - err = writer.Write(doc) + err = writer.Write(ctx, doc) c.Assert(err, qt.IsNil) } err = writer.Finalize() @@ -142,4 +144,8 @@ func TestDocSet_WriteOutputs(t *testing.T) { } var minimalSpec = `--- +openapi: 3.0.3 +info: + title: minimal spec + version: 1.0.0 paths: {}` diff --git a/testdata/catalog-vervet-apis-with-pivot.yaml b/testdata/catalog-vervet-apis-with-pivot.yaml index fc16c5be..2eb5ea08 100644 --- a/testdata/catalog-vervet-apis-with-pivot.yaml +++ b/testdata/catalog-vervet-apis-with-pivot.yaml @@ -192,3 +192,22 @@ spec: owner: someone-else definition: $text: output/2023-06-03~experimental/spec.json +--- +# Generated by vervet, DO NOT EDIT +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + name: Registry_2024-10-15 + title: Registry 2024-10-15 + annotations: + api.snyk.io/generated-by: vervet + labels: + api.snyk.io/version-date: "2024-10-15" + tags: + - 2024-10 +spec: + type: openapi + lifecycle: production + owner: someone-else + definition: + $text: output/2024-10-15/spec.json diff --git a/testdata/catalog-vervet-apis.yaml b/testdata/catalog-vervet-apis.yaml index 11a3e7c4..ceaacf7c 100644 --- a/testdata/catalog-vervet-apis.yaml +++ b/testdata/catalog-vervet-apis.yaml @@ -204,3 +204,26 @@ spec: owner: someone-else definition: $text: output/2023-06-03~experimental/spec.json +--- +# Generated by vervet, DO NOT EDIT +apiVersion: backstage.io/v1alpha1 +kind: API +metadata: + name: Registry_2024-10-15_ga + title: Registry 2024-10-15 ga + annotations: + api.snyk.io/generated-by: vervet + labels: + api.snyk.io/version-date: "2024-10-15" + api.snyk.io/version-lifecycle: released + api.snyk.io/version-stability: ga + tags: + - 2024-10 + - ga + - released +spec: + type: openapi + lifecycle: ga + owner: someone-else + definition: + $text: output/2024-10-15/spec.json diff --git a/testdata/output/2024-10-15/spec.json b/testdata/output/2024-10-15/spec.json new file mode 100644 index 00000000..278c9ef8 --- /dev/null +++ b/testdata/output/2024-10-15/spec.json @@ -0,0 +1,772 @@ +{ + "components": { + "headers": { + "DeprecationHeader": { + "description": "A header containing the deprecation date of the underlying endpoint. For more information, please refer to the deprecation header RFC:\nhttps://tools.ietf.org/id/draft-dalal-deprecation-header-01.html\n", + "example": "2021-07-01T00:00:00Z", + "schema": { + "format": "date-time", + "type": "string" + } + }, + "LocationHeader": { + "description": "A header providing a URL for the location of a resource\n", + "example": "https://example.com/resource/4", + "schema": { + "format": "url", + "type": "string" + } + }, + "RequestIdResponseHeader": { + "description": "A header containing a unique id used for tracking this request. If you are reporting an issue to Snyk it's very helpful to provide this ID.\n", + "example": "4b58e274-ec62-4fab-917b-1d2c48d6bdef", + "schema": { + "format": "uuid", + "type": "string" + } + }, + "SunsetHeader": { + "description": "A header containing the date of when the underlying endpoint will be removed. This header is only present if the endpoint has been deprecated. Please refer to the RFC for more information:\nhttps://datatracker.ietf.org/doc/html/rfc8594\n", + "example": "2021-08-02T00:00:00Z", + "schema": { + "format": "date-time", + "type": "string" + } + }, + "VersionRequestedResponseHeader": { + "description": "A header containing the version of the endpoint requested by the caller.", + "example": "2021-06-04", + "schema": { + "$ref": "#/components/schemas/QueryVersion" + } + }, + "VersionServedResponseHeader": { + "description": "A header containing the version of the endpoint that was served by the API.", + "example": "2021-06-04", + "schema": { + "$ref": "#/components/schemas/ActualVersion" + } + }, + "VersionStageResponseHeader": { + "description": "A header containing the version stage of the endpoint. This stage describes the guarantees snyk provides surrounding stability of the endpoint.\n", + "schema": { + "enum": [ + "wip", + "experimental", + "beta", + "ga", + "deprecated", + "sunset" + ], + "example": "ga", + "type": "string" + } + } + }, + "parameters": { + "Pagination": { + "description": "The parameters used to paginate through a list of results from the API.", + "in": "query", + "name": "page", + "schema": { + "additionalProperties": false, + "properties": { + "after": { + "type": "string" + }, + "before": { + "type": "string" + }, + "size": { + "format": "int32", + "type": "integer" + } + }, + "type": "object" + } + }, + "Version": { + "description": "The requested version of the endpoint to process the request", + "example": "2021-06-04", + "in": "query", + "name": "version", + "required": true, + "schema": { + "$ref": "#/components/schemas/QueryVersion" + } + } + }, + "responses": { + "400": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/ErrorDocument" + } + } + }, + "description": "Bad Request: A parameter provided as a part of the request was invalid.", + "headers": { + "deprecation": { + "$ref": "#/components/headers/DeprecationHeader" + }, + "snyk-request-id": { + "$ref": "#/components/headers/RequestIdResponseHeader" + }, + "snyk-version-lifecycle-stage": { + "$ref": "#/components/headers/VersionStageResponseHeader" + }, + "snyk-version-requested": { + "$ref": "#/components/headers/VersionRequestedResponseHeader" + }, + "snyk-version-served": { + "$ref": "#/components/headers/VersionServedResponseHeader" + }, + "sunset": { + "$ref": "#/components/headers/SunsetHeader" + } + } + }, + "401": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/ErrorDocument" + } + } + }, + "description": "Unauthorized: the request requires an authentication token or a token with more permissions.", + "headers": { + "deprecation": { + "$ref": "#/components/headers/DeprecationHeader" + }, + "snyk-request-id": { + "$ref": "#/components/headers/RequestIdResponseHeader" + }, + "snyk-version-lifecycle-stage": { + "$ref": "#/components/headers/VersionStageResponseHeader" + }, + "snyk-version-requested": { + "$ref": "#/components/headers/VersionRequestedResponseHeader" + }, + "snyk-version-served": { + "$ref": "#/components/headers/VersionServedResponseHeader" + }, + "sunset": { + "$ref": "#/components/headers/SunsetHeader" + } + } + }, + "404": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/ErrorDocument" + } + } + }, + "description": "Not Found: The resource being operated on could not be found.", + "headers": { + "deprecation": { + "$ref": "#/components/headers/DeprecationHeader" + }, + "snyk-request-id": { + "$ref": "#/components/headers/RequestIdResponseHeader" + }, + "snyk-version-lifecycle-stage": { + "$ref": "#/components/headers/VersionStageResponseHeader" + }, + "snyk-version-requested": { + "$ref": "#/components/headers/VersionRequestedResponseHeader" + }, + "snyk-version-served": { + "$ref": "#/components/headers/VersionServedResponseHeader" + }, + "sunset": { + "$ref": "#/components/headers/SunsetHeader" + } + } + }, + "500": { + "content": { + "application/vnd.api+json": { + "schema": { + "$ref": "#/components/schemas/ErrorDocument" + } + } + }, + "description": "Internal Server Error: An error was encountered while attempting to process the request.", + "headers": { + "deprecation": { + "$ref": "#/components/headers/DeprecationHeader" + }, + "snyk-request-id": { + "$ref": "#/components/headers/RequestIdResponseHeader" + }, + "snyk-version-lifecycle-stage": { + "$ref": "#/components/headers/VersionStageResponseHeader" + }, + "snyk-version-requested": { + "$ref": "#/components/headers/VersionRequestedResponseHeader" + }, + "snyk-version-served": { + "$ref": "#/components/headers/VersionServedResponseHeader" + }, + "sunset": { + "$ref": "#/components/headers/SunsetHeader" + } + } + } + }, + "schemas": { + "ActualVersion": { + "description": "Resolved API version", + "pattern": "^((([0-9]{4})-([0-1][0-9]))-((3[01])|(0[1-9])|([12][0-9]))(~(wip|work-in-progress|experimental|beta))?)$", + "type": "string" + }, + "Error": { + "additionalProperties": false, + "example": { + "detail": "Not Found", + "status": "404" + }, + "properties": { + "detail": { + "description": "A human-readable explanation specific to this occurrence of the problem.", + "example": "The request was missing these required fields: ...", + "type": "string" + }, + "id": { + "description": "A unique identifier for this particular occurrence of the problem.", + "example": "f16c31b5-6129-4571-add8-d589da9be524", + "format": "uuid", + "type": "string" + }, + "meta": { + "additionalProperties": true, + "example": { + "key": "value" + }, + "type": "object" + }, + "source": { + "additionalProperties": false, + "example": { + "pointer": "/data/attributes" + }, + "properties": { + "parameter": { + "description": "A string indicating which URI query parameter caused the error.", + "example": "param1", + "type": "string" + }, + "pointer": { + "description": "A JSON Pointer [RFC6901] to the associated entity in the request document.", + "example": "/data/attributes", + "type": "string" + } + }, + "type": "object" + }, + "status": { + "description": "The HTTP status code applicable to this problem, expressed as a string value.", + "example": "400", + "pattern": "^[45]\\d\\d$", + "type": "string" + } + }, + "required": [ + "status", + "detail" + ], + "type": "object" + }, + "ErrorDocument": { + "additionalProperties": false, + "example": { + "errors": [ + { + "detail": "Permission denied for this resource", + "status": "403" + } + ], + "jsonapi": { + "version": "1.0" + } + }, + "properties": { + "errors": { + "example": [ + { + "detail": "Permission denied for this resource", + "status": "403" + } + ], + "items": { + "$ref": "#/components/schemas/Error" + }, + "minItems": 1, + "type": "array" + }, + "jsonapi": { + "$ref": "#/components/schemas/JsonApi" + } + }, + "required": [ + "jsonapi", + "errors" + ], + "type": "object" + }, + "HelloWorld": { + "additionalProperties": false, + "properties": { + "attributes": { + "additionalProperties": false, + "properties": { + "message": { + "type": "string" + }, + "requestSubject": { + "additionalProperties": false, + "properties": { + "clientId": { + "format": "uuid", + "type": "string" + }, + "publicId": { + "format": "uuid", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "publicId", + "type" + ], + "type": "object" + } + }, + "required": [ + "message", + "requestSubject" + ], + "type": "object" + }, + "id": { + "format": "uuid", + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "type", + "id", + "attributes" + ], + "type": "object" + }, + "JsonApi": { + "additionalProperties": false, + "example": { + "version": "1.0" + }, + "properties": { + "version": { + "description": "Version of the JSON API specification this server supports.", + "example": "1.0", + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$", + "type": "string" + } + }, + "required": [ + "version" + ], + "type": "object" + }, + "LinkProperty": { + "example": "https://example.com/api/resource", + "oneOf": [ + { + "description": "A string containing the link’s URL.", + "example": "https://example.com/api/resource", + "type": "string" + }, + { + "additionalProperties": false, + "example": { + "href": "https://example.com/api/resource" + }, + "properties": { + "href": { + "description": "A string containing the link’s URL.", + "example": "https://example.com/api/resource", + "type": "string" + }, + "meta": { + "$ref": "#/components/schemas/Meta" + } + }, + "required": [ + "href" + ], + "type": "object" + } + ] + }, + "Links": { + "additionalProperties": false, + "properties": { + "first": { + "$ref": "#/components/schemas/LinkProperty" + }, + "last": { + "$ref": "#/components/schemas/LinkProperty" + }, + "next": { + "$ref": "#/components/schemas/LinkProperty" + }, + "prev": { + "$ref": "#/components/schemas/LinkProperty" + }, + "related": { + "$ref": "#/components/schemas/LinkProperty" + }, + "self": { + "$ref": "#/components/schemas/LinkProperty" + } + }, + "type": "object" + }, + "Meta": { + "additionalProperties": true, + "description": "Free-form object that may contain non-standard information.", + "example": { + "key1": "value1", + "key2": { + "sub_key": "sub_value" + }, + "key3": [ + "array_value1", + "array_value2" + ] + }, + "type": "object" + }, + "QueryVersion": { + "description": "Requested API version", + "pattern": "^(wip|work-in-progress|experimental|beta|((([0-9]{4})-([0-1][0-9]))-((3[01])|(0[1-9])|([12][0-9]))(~(wip|work-in-progress|experimental|beta))?))$", + "type": "string" + } + } + }, + "info": { + "title": "Registry", + "version": "3.0.0" + }, + "openapi": "3.0.3", + "paths": { + "/examples/hello-world": { + "post": { + "description": "Create a single result from the hello-world example", + "operationId": "helloWorldCreate", + "parameters": [ + { + "$ref": "#/components/parameters/Version" + } + ], + "requestBody": { + "content": { + "application/vnd.api+json": { + "schema": { + "additionalProperties": false, + "properties": { + "attributes": { + "additionalProperties": false, + "properties": { + "betaField": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": [ + "message", + "betaField" + ], + "type": "object" + } + }, + "required": [ + "attributes" + ], + "type": "object" + } + } + } + }, + "responses": { + "201": { + "content": { + "application/vnd.api+json": { + "schema": { + "additionalProperties": false, + "properties": { + "data": { + "$ref": "#/components/schemas/HelloWorld" + }, + "jsonapi": { + "$ref": "#/components/schemas/JsonApi" + }, + "links": { + "$ref": "#/components/schemas/Links" + } + }, + "required": [ + "jsonapi", + "data", + "links" + ], + "type": "object" + } + } + }, + "description": "A hello world entity being requested is returned", + "headers": { + "location": { + "$ref": "#/components/headers/LocationHeader" + } + }, + "x-snyk-include-headers": { + "$ref": "../../../schemas/headers/common-response.yaml#/Common" + } + }, + "400": { + "$ref": "#/components/responses/400" + }, + "401": { + "$ref": "#/components/responses/401" + }, + "404": { + "$ref": "#/components/responses/404" + }, + "500": { + "$ref": "#/components/responses/500" + } + }, + "x-snyk-api-lifecycle": "released", + "x-snyk-api-owners": [ + "@snyk/api" + ], + "x-snyk-api-releases": [ + "2021-06-13~beta" + ], + "x-snyk-api-resource": "hello-world", + "x-snyk-api-stability": "beta", + "x-snyk-api-version": "2021-06-13~beta", + "x-stability-level": "beta" + } + }, + "/examples/hello-world/{id}": { + "get": { + "description": "Get a single result from the hello-world example", + "operationId": "helloWorldGetOne", + "parameters": [ + { + "$ref": "#/components/parameters/Version" + }, + { + "$ref": "#/components/parameters/Pagination" + }, + { + "description": "The id of the hello-world example entity to be retrieved.", + "in": "path", + "name": "id", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/vnd.api+json": { + "schema": { + "additionalProperties": false, + "properties": { + "data": { + "$ref": "#/components/schemas/HelloWorld" + }, + "jsonapi": { + "$ref": "#/components/schemas/JsonApi" + }, + "links": { + "$ref": "#/components/schemas/Links" + } + }, + "required": [ + "jsonapi", + "data", + "links" + ], + "type": "object" + } + } + }, + "description": "A hello world entity being requested is returned", + "headers": { + "snyk-request-id": { + "$ref": "#/components/headers/RequestIdResponseHeader" + }, + "snyk-version-requested": { + "$ref": "#/components/headers/VersionRequestedResponseHeader" + }, + "snyk-version-served": { + "$ref": "#/components/headers/VersionServedResponseHeader" + } + } + }, + "400": { + "$ref": "#/components/responses/400" + }, + "401": { + "$ref": "#/components/responses/401" + }, + "404": { + "$ref": "#/components/responses/404" + }, + "500": { + "$ref": "#/components/responses/500" + } + }, + "x-snyk-api-lifecycle": "released", + "x-snyk-api-owners": [ + "@snyk/api" + ], + "x-snyk-api-releases": [ + "2021-06-01~experimental", + "2021-06-07~experimental", + "2021-06-13~beta" + ], + "x-snyk-api-resource": "hello-world", + "x-snyk-api-stability": "beta", + "x-snyk-api-version": "2021-06-13~beta", + "x-stability-level": "beta" + } + }, + "/openapi": { + "get": { + "description": "List available versions of OpenAPI specification", + "operationId": "listAPIVersions", + "responses": { + "200": { + "content": { + "application/vnd.api+json": { + "schema": { + "items": { + "type": "string" + }, + "type": "array" + } + } + }, + "description": "List of available versions is returned", + "headers": { + "snyk-request-id": { + "$ref": "#/components/headers/RequestIdResponseHeader" + }, + "snyk-version-requested": { + "$ref": "#/components/headers/VersionRequestedResponseHeader" + }, + "snyk-version-served": { + "$ref": "#/components/headers/VersionServedResponseHeader" + } + } + }, + "400": { + "$ref": "#/components/responses/400" + }, + "401": { + "$ref": "#/components/responses/401" + }, + "404": { + "$ref": "#/components/responses/404" + }, + "500": { + "$ref": "#/components/responses/500" + } + } + } + }, + "/openapi/{version}": { + "get": { + "description": "Get OpenAPI specification effective at version.", + "operationId": "getAPIVersion", + "parameters": [ + { + "description": "The requested version of the API", + "in": "path", + "name": "version", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "application/vnd.api+json": { + "schema": { + "type": "object" + } + }, + "application/x-yaml": { + "schema": { + "type": "object" + } + } + }, + "description": "OpenAPI specification matching requested version is returned", + "headers": { + "snyk-request-id": { + "$ref": "#/components/headers/RequestIdResponseHeader" + }, + "snyk-version-requested": { + "$ref": "#/components/headers/VersionRequestedResponseHeader" + }, + "snyk-version-served": { + "$ref": "#/components/headers/VersionServedResponseHeader" + } + } + }, + "400": { + "$ref": "#/components/responses/400" + }, + "401": { + "$ref": "#/components/responses/401" + }, + "404": { + "$ref": "#/components/responses/404" + }, + "500": { + "$ref": "#/components/responses/500" + } + } + } + } + }, + "servers": [ + { + "description": "Test REST API", + "url": "https://example.com/api/rest" + } + ], + "x-snyk-api-version": "2024-10-15" +} \ No newline at end of file diff --git a/testdata/output/2024-10-15/spec.yaml b/testdata/output/2024-10-15/spec.yaml new file mode 100644 index 00000000..ce5db982 --- /dev/null +++ b/testdata/output/2024-10-15/spec.yaml @@ -0,0 +1,536 @@ +# OpenAPI spec generated by vervet, DO NOT EDIT +components: + headers: + DeprecationHeader: + description: | + A header containing the deprecation date of the underlying endpoint. For more information, please refer to the deprecation header RFC: + https://tools.ietf.org/id/draft-dalal-deprecation-header-01.html + example: "2021-07-01T00:00:00Z" + schema: + format: date-time + type: string + LocationHeader: + description: | + A header providing a URL for the location of a resource + example: https://example.com/resource/4 + schema: + format: url + type: string + RequestIdResponseHeader: + description: | + A header containing a unique id used for tracking this request. If you are reporting an issue to Snyk it's very helpful to provide this ID. + example: 4b58e274-ec62-4fab-917b-1d2c48d6bdef + schema: + format: uuid + type: string + SunsetHeader: + description: | + A header containing the date of when the underlying endpoint will be removed. This header is only present if the endpoint has been deprecated. Please refer to the RFC for more information: + https://datatracker.ietf.org/doc/html/rfc8594 + example: "2021-08-02T00:00:00Z" + schema: + format: date-time + type: string + VersionRequestedResponseHeader: + description: A header containing the version of the endpoint requested by the + caller. + example: "2021-06-04" + schema: + $ref: '#/components/schemas/QueryVersion' + VersionServedResponseHeader: + description: A header containing the version of the endpoint that was served + by the API. + example: "2021-06-04" + schema: + $ref: '#/components/schemas/ActualVersion' + VersionStageResponseHeader: + description: | + A header containing the version stage of the endpoint. This stage describes the guarantees snyk provides surrounding stability of the endpoint. + schema: + enum: + - wip + - experimental + - beta + - ga + - deprecated + - sunset + example: ga + type: string + parameters: + Pagination: + description: The parameters used to paginate through a list of results from + the API. + in: query + name: page + schema: + additionalProperties: false + properties: + after: + type: string + before: + type: string + size: + format: int32 + type: integer + type: object + Version: + description: The requested version of the endpoint to process the request + example: "2021-06-04" + in: query + name: version + required: true + schema: + $ref: '#/components/schemas/QueryVersion' + responses: + "400": + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ErrorDocument' + description: 'Bad Request: A parameter provided as a part of the request was + invalid.' + headers: + deprecation: + $ref: '#/components/headers/DeprecationHeader' + snyk-request-id: + $ref: '#/components/headers/RequestIdResponseHeader' + snyk-version-lifecycle-stage: + $ref: '#/components/headers/VersionStageResponseHeader' + snyk-version-requested: + $ref: '#/components/headers/VersionRequestedResponseHeader' + snyk-version-served: + $ref: '#/components/headers/VersionServedResponseHeader' + sunset: + $ref: '#/components/headers/SunsetHeader' + "401": + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ErrorDocument' + description: 'Unauthorized: the request requires an authentication token or + a token with more permissions.' + headers: + deprecation: + $ref: '#/components/headers/DeprecationHeader' + snyk-request-id: + $ref: '#/components/headers/RequestIdResponseHeader' + snyk-version-lifecycle-stage: + $ref: '#/components/headers/VersionStageResponseHeader' + snyk-version-requested: + $ref: '#/components/headers/VersionRequestedResponseHeader' + snyk-version-served: + $ref: '#/components/headers/VersionServedResponseHeader' + sunset: + $ref: '#/components/headers/SunsetHeader' + "404": + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ErrorDocument' + description: 'Not Found: The resource being operated on could not be found.' + headers: + deprecation: + $ref: '#/components/headers/DeprecationHeader' + snyk-request-id: + $ref: '#/components/headers/RequestIdResponseHeader' + snyk-version-lifecycle-stage: + $ref: '#/components/headers/VersionStageResponseHeader' + snyk-version-requested: + $ref: '#/components/headers/VersionRequestedResponseHeader' + snyk-version-served: + $ref: '#/components/headers/VersionServedResponseHeader' + sunset: + $ref: '#/components/headers/SunsetHeader' + "500": + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/ErrorDocument' + description: 'Internal Server Error: An error was encountered while attempting + to process the request.' + headers: + deprecation: + $ref: '#/components/headers/DeprecationHeader' + snyk-request-id: + $ref: '#/components/headers/RequestIdResponseHeader' + snyk-version-lifecycle-stage: + $ref: '#/components/headers/VersionStageResponseHeader' + snyk-version-requested: + $ref: '#/components/headers/VersionRequestedResponseHeader' + snyk-version-served: + $ref: '#/components/headers/VersionServedResponseHeader' + sunset: + $ref: '#/components/headers/SunsetHeader' + schemas: + ActualVersion: + description: Resolved API version + pattern: ^((([0-9]{4})-([0-1][0-9]))-((3[01])|(0[1-9])|([12][0-9]))(~(wip|work-in-progress|experimental|beta))?)$ + type: string + Error: + additionalProperties: false + example: + detail: Not Found + status: "404" + properties: + detail: + description: A human-readable explanation specific to this occurrence of + the problem. + example: 'The request was missing these required fields: ...' + type: string + id: + description: A unique identifier for this particular occurrence of the problem. + example: f16c31b5-6129-4571-add8-d589da9be524 + format: uuid + type: string + meta: + additionalProperties: true + example: + key: value + type: object + source: + additionalProperties: false + example: + pointer: /data/attributes + properties: + parameter: + description: A string indicating which URI query parameter caused the + error. + example: param1 + type: string + pointer: + description: A JSON Pointer [RFC6901] to the associated entity in the + request document. + example: /data/attributes + type: string + type: object + status: + description: The HTTP status code applicable to this problem, expressed + as a string value. + example: "400" + pattern: ^[45]\d\d$ + type: string + required: + - status + - detail + type: object + ErrorDocument: + additionalProperties: false + example: + errors: + - detail: Permission denied for this resource + status: "403" + jsonapi: + version: "1.0" + properties: + errors: + example: + - detail: Permission denied for this resource + status: "403" + items: + $ref: '#/components/schemas/Error' + minItems: 1 + type: array + jsonapi: + $ref: '#/components/schemas/JsonApi' + required: + - jsonapi + - errors + type: object + HelloWorld: + additionalProperties: false + properties: + attributes: + additionalProperties: false + properties: + message: + type: string + requestSubject: + additionalProperties: false + properties: + clientId: + format: uuid + type: string + publicId: + format: uuid + type: string + type: + type: string + required: + - publicId + - type + type: object + required: + - message + - requestSubject + type: object + id: + format: uuid + type: string + type: + type: string + required: + - type + - id + - attributes + type: object + JsonApi: + additionalProperties: false + example: + version: "1.0" + properties: + version: + description: Version of the JSON API specification this server supports. + example: "1.0" + pattern: ^(0|[1-9]\d*)\.(0|[1-9]\d*)$ + type: string + required: + - version + type: object + LinkProperty: + example: https://example.com/api/resource + oneOf: + - description: A string containing the link’s URL. + example: https://example.com/api/resource + type: string + - additionalProperties: false + example: + href: https://example.com/api/resource + properties: + href: + description: A string containing the link’s URL. + example: https://example.com/api/resource + type: string + meta: + $ref: '#/components/schemas/Meta' + required: + - href + type: object + Links: + additionalProperties: false + properties: + first: + $ref: '#/components/schemas/LinkProperty' + last: + $ref: '#/components/schemas/LinkProperty' + next: + $ref: '#/components/schemas/LinkProperty' + prev: + $ref: '#/components/schemas/LinkProperty' + related: + $ref: '#/components/schemas/LinkProperty' + self: + $ref: '#/components/schemas/LinkProperty' + type: object + Meta: + additionalProperties: true + description: Free-form object that may contain non-standard information. + example: + key1: value1 + key2: + sub_key: sub_value + key3: + - array_value1 + - array_value2 + type: object + QueryVersion: + description: Requested API version + pattern: ^(wip|work-in-progress|experimental|beta|((([0-9]{4})-([0-1][0-9]))-((3[01])|(0[1-9])|([12][0-9]))(~(wip|work-in-progress|experimental|beta))?))$ + type: string +info: + title: Registry + version: 3.0.0 +openapi: 3.0.3 +paths: + /examples/hello-world: + post: + description: Create a single result from the hello-world example + operationId: helloWorldCreate + parameters: + - $ref: '#/components/parameters/Version' + requestBody: + content: + application/vnd.api+json: + schema: + additionalProperties: false + properties: + attributes: + additionalProperties: false + properties: + betaField: + type: string + message: + type: string + required: + - message + - betaField + type: object + required: + - attributes + type: object + responses: + "201": + content: + application/vnd.api+json: + schema: + additionalProperties: false + properties: + data: + $ref: '#/components/schemas/HelloWorld' + jsonapi: + $ref: '#/components/schemas/JsonApi' + links: + $ref: '#/components/schemas/Links' + required: + - jsonapi + - data + - links + type: object + description: A hello world entity being requested is returned + headers: + location: + $ref: '#/components/headers/LocationHeader' + x-snyk-include-headers: + $ref: ../../../schemas/headers/common-response.yaml#/Common + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "404": + $ref: '#/components/responses/404' + "500": + $ref: '#/components/responses/500' + x-snyk-api-lifecycle: released + x-snyk-api-owners: + - '@snyk/api' + x-snyk-api-releases: + - 2021-06-13~beta + x-snyk-api-resource: hello-world + x-snyk-api-stability: beta + x-snyk-api-version: 2021-06-13~beta + x-stability-level: beta + /examples/hello-world/{id}: + get: + description: Get a single result from the hello-world example + operationId: helloWorldGetOne + parameters: + - $ref: '#/components/parameters/Version' + - $ref: '#/components/parameters/Pagination' + - description: The id of the hello-world example entity to be retrieved. + in: path + name: id + required: true + schema: + type: string + responses: + "200": + content: + application/vnd.api+json: + schema: + additionalProperties: false + properties: + data: + $ref: '#/components/schemas/HelloWorld' + jsonapi: + $ref: '#/components/schemas/JsonApi' + links: + $ref: '#/components/schemas/Links' + required: + - jsonapi + - data + - links + type: object + description: A hello world entity being requested is returned + headers: + snyk-request-id: + $ref: '#/components/headers/RequestIdResponseHeader' + snyk-version-requested: + $ref: '#/components/headers/VersionRequestedResponseHeader' + snyk-version-served: + $ref: '#/components/headers/VersionServedResponseHeader' + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "404": + $ref: '#/components/responses/404' + "500": + $ref: '#/components/responses/500' + x-snyk-api-lifecycle: released + x-snyk-api-owners: + - '@snyk/api' + x-snyk-api-releases: + - 2021-06-01~experimental + - 2021-06-07~experimental + - 2021-06-13~beta + x-snyk-api-resource: hello-world + x-snyk-api-stability: beta + x-snyk-api-version: 2021-06-13~beta + x-stability-level: beta + /openapi: + get: + description: List available versions of OpenAPI specification + operationId: listAPIVersions + responses: + "200": + content: + application/vnd.api+json: + schema: + items: + type: string + type: array + description: List of available versions is returned + headers: + snyk-request-id: + $ref: '#/components/headers/RequestIdResponseHeader' + snyk-version-requested: + $ref: '#/components/headers/VersionRequestedResponseHeader' + snyk-version-served: + $ref: '#/components/headers/VersionServedResponseHeader' + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "404": + $ref: '#/components/responses/404' + "500": + $ref: '#/components/responses/500' + /openapi/{version}: + get: + description: Get OpenAPI specification effective at version. + operationId: getAPIVersion + parameters: + - description: The requested version of the API + in: path + name: version + required: true + schema: + type: string + responses: + "200": + content: + application/vnd.api+json: + schema: + type: object + application/x-yaml: + schema: + type: object + description: OpenAPI specification matching requested version is returned + headers: + snyk-request-id: + $ref: '#/components/headers/RequestIdResponseHeader' + snyk-version-requested: + $ref: '#/components/headers/VersionRequestedResponseHeader' + snyk-version-served: + $ref: '#/components/headers/VersionServedResponseHeader' + "400": + $ref: '#/components/responses/400' + "401": + $ref: '#/components/responses/401' + "404": + $ref: '#/components/responses/404' + "500": + $ref: '#/components/responses/500' +servers: +- description: Test REST API + url: https://example.com/api/rest +x-snyk-api-version: "2024-10-15" diff --git a/testdata/output/embed.go b/testdata/output/embed.go index 0b561ca7..fafcc073 100644 --- a/testdata/output/embed.go +++ b/testdata/output/embed.go @@ -12,10 +12,10 @@ import "embed" //go:embed 2021-06-04~experimental/spec.yaml //go:embed 2021-06-07~experimental/spec.json //go:embed 2021-06-07~experimental/spec.yaml -//go:embed 2021-06-13~experimental/spec.json -//go:embed 2021-06-13~experimental/spec.yaml //go:embed 2021-06-13~beta/spec.json //go:embed 2021-06-13~beta/spec.yaml +//go:embed 2021-06-13~experimental/spec.json +//go:embed 2021-06-13~experimental/spec.yaml //go:embed 2021-08-20~experimental/spec.json //go:embed 2021-08-20~experimental/spec.yaml //go:embed 2023-06-01~experimental/spec.json @@ -24,6 +24,8 @@ import "embed" //go:embed 2023-06-02~experimental/spec.yaml //go:embed 2023-06-03~experimental/spec.json //go:embed 2023-06-03~experimental/spec.yaml +//go:embed 2024-10-15/spec.json +//go:embed 2024-10-15/spec.yaml // Versions contains OpenAPI specs for each distinct release version. var Versions embed.FS