Skip to content

Commit

Permalink
fix(type-safe-api): resolve parameter refs prior to parameter validation
Browse files Browse the repository at this point in the history
It is legal to use $ref in parameters in an OpenAPI spec, but the validation was assuming these were
not present. Updated the validation to work on a dereferenced clone of the spec. Ensured that the
final parsed spec retains references to ensure the code generator can consolidate models.

Fixes #590
  • Loading branch information
cogwirrel committed Oct 4, 2023
1 parent a84d746 commit b85c759
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import SwaggerParser from "@apidevtools/swagger-parser";
import { writeFile } from "projen/lib/util";
import { parse } from "ts-command-line-args";
import * as path from 'path';
import * as _ from "lodash";
import fs from "fs";
import type { OpenAPIV3 } from "openapi-types";

// Smithy HTTP trait is used to map Smithy operations to their location in the spec
const SMITHY_HTTP_TRAIT_ID = "smithy.api#http";
Expand Down Expand Up @@ -49,6 +51,7 @@ interface Arguments {
readonly outputPath: string;
}


void (async () => {
const args = parse<Arguments>({
specPath: { type: String, alias: "s" },
Expand Down Expand Up @@ -101,8 +104,16 @@ void (async () => {

const invalidRequestParameters: InvalidRequestParameter[] = [];

// Dereference a clone of the spec to test parameters
const dereferencedSpec = await SwaggerParser.dereference(JSON.parse(JSON.stringify(spec)), {
dereference: {
// Circular references are valid, we just ignore them for the purpose of validation
circular: "ignore",
},
});

// Validate the request parameters
Object.entries(spec.paths || {}).forEach(([p, pathOp]: [string, any]) => {
Object.entries(dereferencedSpec.paths || {}).forEach(([p, pathOp]: [string, any]) => {
Object.entries(pathOp ?? {}).forEach(([method, operation]: [string, any]) => {
(operation?.parameters ?? []).forEach((parameter: any) => {
// Check if the parameter is an allowed type
Expand Down
39 changes: 39 additions & 0 deletions packages/type-safe-api/test/resources/specs/parameter-refs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
openapi: 3.0.3
info:
version: 1.0.0
title: Example API
paths:
/hello:
get:
operationId: sayHello
x-handler:
language: typescript
parameters:
- $ref: '#/components/parameters/HelloId'
responses:
'200':
description: Successful response
content:
'application/json':
schema:
$ref: '#/components/schemas/HelloResponse'
components:
parameters:
HelloId:
in: query
name: id
schema:
$ref: '#/components/schemas/HelloId'
required: false
schemas:
HelloId:
type: string
HelloResponse:
type: object
properties:
id:
$ref: '#/components/schemas/HelloId'
message:
$ref: '#/components/schemas/HelloResponse'
required:
- id

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,20 @@ describe("Parse OpenAPI Spec Script Unit Tests", () => {
);
});
});

it("Permits parameter references (and circular references)", () => {
expect(
withTmpDirSnapshot(os.tmpdir(), (tmpDir) => {
const specPath = "../../resources/specs/parameter-refs.yaml";
const outputPath = path.join(
path.relative(path.resolve(__dirname), tmpDir),
".api.json"
);
const command = `../../../scripts/type-safe-api/parser/parse-openapi-spec --spec-path ${specPath} --output-path ${outputPath}`;
exec(command, {
cwd: path.resolve(__dirname),
});
})
).toMatchSnapshot();
});
});

0 comments on commit b85c759

Please sign in to comment.