-
Notifications
You must be signed in to change notification settings - Fork 14
CLOUDP-302982: IPA-104 - Get object should have suffix "Response" #465
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
7779268
4982da0
a2e127c
95ec622
c0f679d
53b04b8
b8bb4ea
f814fa6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,263 @@ | ||||||
| import testRule from './__helpers__/testRule'; | ||||||
| import { DiagnosticSeverity } from '@stoplight/types'; | ||||||
|
|
||||||
| const componentSchemas = { | ||||||
| schemas: { | ||||||
| SchemaResponse: { | ||||||
| type: 'object', | ||||||
| }, | ||||||
| Schema: { | ||||||
| type: 'object', | ||||||
| }, | ||||||
| }, | ||||||
| }; | ||||||
|
|
||||||
| testRule('xgen-IPA-104-get-method-returns-response-suffixed-object', [ | ||||||
| { | ||||||
| name: 'valid schema names names', | ||||||
| document: { | ||||||
| paths: { | ||||||
| '/resource/{id}': { | ||||||
| get: { | ||||||
| responses: { | ||||||
| 200: { | ||||||
| content: { | ||||||
| 'application/vnd.atlas.2023-01-01+json': { | ||||||
| schema: { | ||||||
| $ref: '#/components/schemas/SchemaResponse', | ||||||
| }, | ||||||
| }, | ||||||
| 'application/vnd.atlas.2024-01-01+json': { | ||||||
| schema: { | ||||||
| $ref: '#/components/schemas/SchemaResponse', | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What would be our approach for inline schemas (properties defined here). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently the rule will fail and tell the API producer to implement a schema ref, same approach as we did in #473 |
||||||
| }, | ||||||
| }, | ||||||
| 'application/vnd.atlas.2025-01-01+json': { | ||||||
| schema: { | ||||||
| type: 'array', | ||||||
| items: { | ||||||
| $ref: '#/components/schemas/SchemaResponse', | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| 400: { | ||||||
| content: { | ||||||
| 'application/vnd.atlas.2023-01-01+json': { | ||||||
| schema: { | ||||||
| $ref: '#/components/schemas/Schema', | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| '/resourceTwo/{id}': { | ||||||
| get: { | ||||||
| responses: { | ||||||
| 200: { | ||||||
| content: { | ||||||
| 'application/vnd.atlas.2024-08-05+json': { | ||||||
| type: 'string', | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| '/resource/{id}/singleton': { | ||||||
| get: { | ||||||
| responses: { | ||||||
| 200: { | ||||||
| content: { | ||||||
| 'application/vnd.atlas.2024-08-05+json': { | ||||||
| schema: { | ||||||
| $ref: '#/components/schemas/SchemaResponse', | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| components: componentSchemas, | ||||||
| }, | ||||||
| errors: [], | ||||||
| }, | ||||||
| { | ||||||
| name: 'invalid resources', | ||||||
| document: { | ||||||
| paths: { | ||||||
| '/resource/{id}': { | ||||||
| get: { | ||||||
| responses: { | ||||||
| 200: { | ||||||
| content: { | ||||||
| 'application/vnd.atlas.2023-01-01+json': { | ||||||
| schema: { | ||||||
| $ref: '#/components/schemas/Schema', | ||||||
| }, | ||||||
| }, | ||||||
| 'application/vnd.atlas.2024-01-01+json': { | ||||||
| schema: { | ||||||
| $ref: '#/components/schemas/Schema', | ||||||
| }, | ||||||
| }, | ||||||
| 'application/vnd.atlas.2025-01-01+json': { | ||||||
| schema: { | ||||||
| type: 'array', | ||||||
| items: { | ||||||
| $ref: '#/components/schemas/Schema', | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. example to ignore
Suggested change
|
||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| '/resource/{id}/singleton': { | ||||||
| get: { | ||||||
| responses: { | ||||||
| 200: { | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional: Test for two 2xx response codes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That would be an invalid json since we cannot have two keys of the same value |
||||||
| content: { | ||||||
| 'application/vnd.atlas.2024-08-05+json': { | ||||||
| schema: { | ||||||
| $ref: '#/components/schemas/Schema', | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| components: componentSchemas, | ||||||
| }, | ||||||
| errors: [ | ||||||
| { | ||||||
| code: 'xgen-IPA-104-get-method-returns-response-suffixed-object', | ||||||
| message: 'The request schema must reference a schema with a Response suffix. http://go/ipa/104', | ||||||
| path: [ | ||||||
| 'paths', | ||||||
| '/resource/{id}', | ||||||
| 'get', | ||||||
| 'responses', | ||||||
| '200', | ||||||
| 'content', | ||||||
| 'application/vnd.atlas.2023-01-01+json', | ||||||
| ], | ||||||
| severity: DiagnosticSeverity.Warning, | ||||||
| }, | ||||||
| { | ||||||
| code: 'xgen-IPA-104-get-method-returns-response-suffixed-object', | ||||||
| message: 'The request schema must reference a schema with a Response suffix. http://go/ipa/104', | ||||||
| path: [ | ||||||
| 'paths', | ||||||
| '/resource/{id}', | ||||||
| 'get', | ||||||
| 'responses', | ||||||
| '200', | ||||||
| 'content', | ||||||
| 'application/vnd.atlas.2024-01-01+json', | ||||||
| ], | ||||||
| severity: DiagnosticSeverity.Warning, | ||||||
| }, | ||||||
| { | ||||||
| code: 'xgen-IPA-104-get-method-returns-response-suffixed-object', | ||||||
| message: 'The request schema must reference a schema with a Response suffix. http://go/ipa/104', | ||||||
| path: [ | ||||||
| 'paths', | ||||||
| '/resource/{id}', | ||||||
| 'get', | ||||||
| 'responses', | ||||||
| '200', | ||||||
| 'content', | ||||||
| 'application/vnd.atlas.2025-01-01+json', | ||||||
| ], | ||||||
| severity: DiagnosticSeverity.Warning, | ||||||
| }, | ||||||
| { | ||||||
| code: 'xgen-IPA-104-get-method-returns-response-suffixed-object', | ||||||
| message: 'The request schema must reference a schema with a Response suffix. http://go/ipa/104', | ||||||
| path: [ | ||||||
| 'paths', | ||||||
| '/resource/{id}/singleton', | ||||||
| 'get', | ||||||
| 'responses', | ||||||
| '200', | ||||||
| 'content', | ||||||
| 'application/vnd.atlas.2024-08-05+json', | ||||||
| ], | ||||||
| severity: DiagnosticSeverity.Warning, | ||||||
| }, | ||||||
| ], | ||||||
| }, | ||||||
| { | ||||||
| name: 'invalid resources with exceptions', | ||||||
| document: { | ||||||
| paths: { | ||||||
| '/resource/{id}': { | ||||||
| get: { | ||||||
| responses: { | ||||||
| 200: { | ||||||
| content: { | ||||||
| 'application/vnd.atlas.2023-01-01+json': { | ||||||
| 'x-xgen-IPA-exception': { | ||||||
| 'xgen-IPA-104-get-method-returns-response-suffixed-object': 'reason', | ||||||
| }, | ||||||
| schema: { | ||||||
| $ref: '#/components/schemas/Schema', | ||||||
| }, | ||||||
| }, | ||||||
| 'application/vnd.atlas.2024-01-01+json': { | ||||||
| 'x-xgen-IPA-exception': { | ||||||
| 'xgen-IPA-104-get-method-returns-response-suffixed-object': 'reason', | ||||||
| }, | ||||||
| schema: { | ||||||
| $ref: '#/components/schemas/Schema', | ||||||
| }, | ||||||
| }, | ||||||
| 'application/vnd.atlas.2025-01-01+json': { | ||||||
| 'x-xgen-IPA-exception': { | ||||||
| 'xgen-IPA-104-get-method-returns-response-suffixed-object': 'reason', | ||||||
| }, | ||||||
| schema: { | ||||||
| type: 'array', | ||||||
| items: { | ||||||
| $ref: '#/components/schemas/Schema', | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| '/resource/{id}/singleton': { | ||||||
| get: { | ||||||
| responses: { | ||||||
| 200: { | ||||||
| content: { | ||||||
| 'application/vnd.atlas.2024-08-05+json': { | ||||||
| 'x-xgen-IPA-exception': { | ||||||
| 'xgen-IPA-104-get-method-returns-response-suffixed-object': 'reason', | ||||||
| }, | ||||||
| schema: { | ||||||
| $ref: '#/components/schemas/Schema', | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| }, | ||||||
| components: componentSchemas, | ||||||
| }, | ||||||
| errors: [], | ||||||
| }, | ||||||
| ]); | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ import { hasException } from './utils/exceptions.js'; | |
| import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js'; | ||
| import { isCustomMethodIdentifier } from './utils/resourceEvaluation.js'; | ||
| import { resolveObject } from './utils/componentUtils.js'; | ||
| import { getSchemaRef } from './utils/methodUtils.js'; | ||
|
|
||
| const RULE_NAME = 'xgen-IPA-106-create-method-request-body-is-request-suffixed-object'; | ||
| const ERROR_MESSAGE_SCHEMA_NAME = 'The response body schema must reference a schema with a Request suffix.'; | ||
|
|
@@ -23,25 +24,15 @@ export default (input, _, { path, documentInventory }) => { | |
| } | ||
|
|
||
| if (contentPerMediaType.schema) { | ||
| console.log(contentPerMediaType); | ||
| const schema = contentPerMediaType.schema; | ||
| if (schema.type === 'array' && schema.items) { | ||
| let schemaItems = schema.items; | ||
| if (!schemaItems.$ref) { | ||
| return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE_SCHEMA_REF); | ||
| } | ||
| if (!schemaItems.$ref.endsWith('Request')) { | ||
| return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE_SCHEMA_NAME); | ||
| } | ||
| } else { | ||
| if (!schema.$ref) { | ||
| return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE_SCHEMA_REF); | ||
| } | ||
|
|
||
| if (!schema.$ref.endsWith('Request')) { | ||
| return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE_SCHEMA_NAME); | ||
| } | ||
| const schemaRef = getSchemaRef(schema); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Created a shared function since this logic is pretty much the same for the new rule as well There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was looking for that function as well :) |
||
| if (!schemaRef) { | ||
| return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE_SCHEMA_REF); | ||
| } | ||
| if (!schemaRef.endsWith('Request')) { | ||
| return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE_SCHEMA_NAME); | ||
| } | ||
|
|
||
| collectAdoption(path, RULE_NAME); | ||
| } | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| import { | ||
| isSingleResourceIdentifier, | ||
| isSingletonResource, | ||
| getResourcePathItems, | ||
| isResourceCollectionIdentifier, | ||
| } from './utils/resourceEvaluation.js'; | ||
| import { resolveObject } from './utils/componentUtils.js'; | ||
| import { hasException } from './utils/exceptions.js'; | ||
| import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js'; | ||
| import { getSchemaRef } from './utils/methodUtils.js'; | ||
|
|
||
| const RULE_NAME = 'xgen-IPA-104-get-method-returns-response-suffixed-object'; | ||
| const ERROR_MESSAGE_SCHEMA_NAME = 'The request schema must reference a schema with a Response suffix.'; | ||
| const ERROR_MESSAGE_SCHEMA_REF = 'The response body schema is defined inline and must reference a predefined schema.'; | ||
|
|
||
| export default (input, _, { path, documentInventory }) => { | ||
| const resourcePath = path[1]; | ||
| const responseCode = path[4]; | ||
| const oas = documentInventory.unresolved; | ||
| const resourcePaths = getResourcePathItems(resourcePath, oas.paths); | ||
|
|
||
| if ( | ||
| responseCode.startsWith('2') || | ||
| isResourceCollectionIdentifier(resourcePath) || | ||
| (isSingleResourceIdentifier(resourcePath) && isSingletonResource(resourcePaths)) | ||
| ) { | ||
| const contentPerMediaType = resolveObject(oas, path); | ||
|
|
||
| if (hasException(contentPerMediaType, RULE_NAME)) { | ||
| collectException(contentPerMediaType, RULE_NAME, path); | ||
| return; | ||
| } | ||
|
|
||
| if (contentPerMediaType.schema) { | ||
| const schema = contentPerMediaType.schema; | ||
| const schemaRef = getSchemaRef(schema); | ||
| if (!schemaRef) { | ||
| return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE_SCHEMA_REF); | ||
| } | ||
| if (!schemaRef.endsWith('Response')) { | ||
| return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE_SCHEMA_NAME); | ||
| } | ||
| collectAdoption(path, RULE_NAME); | ||
| } | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,3 +29,16 @@ export function getAllSuccessfulResponseSchemas(operationObject) { | |
| }); | ||
| return result; | ||
| } | ||
|
|
||
| /** | ||
| * Gets the schema reference for a schema object. If the schema does not have a reference, undefined is returned. | ||
| * | ||
| * @param {object} schema the schema object | ||
| * @returns {string} the schema ref | ||
| */ | ||
| export function getSchemaRef(schema) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❤️ |
||
| if (schema.type === 'array' && schema.items) { | ||
| return schema.items.$ref; | ||
| } | ||
| return schema.$ref; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technical note to add:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, I'm good with that, can document when merged