-
Couldn't load subscription status.
- Fork 14
CLOUDP-304053: IPA-106:Create - The resource must be the request body (implement deepObjectComparison without third party dependencies) #522
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 9 commits
84f0579
5a98e13
cb8fcb4
6ba5e3b
62054dc
39462eb
2390d68
7b04e62
2017cd1
51cedde
b14115c
fb66eb4
7211337
63fd013
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 |
|---|---|---|
|
|
@@ -16,17 +16,21 @@ rules: | |
| field: '@key' | ||
| function: 'createMethodRequestBodyIsRequestSuffixedObject' | ||
| xgen-IPA-106-create-method-should-not-have-query-parameters: | ||
| description: 'Create operations should not use query parameters. http://go/ipa/xxx' | ||
| description: 'Create operations should not use query parameters. http://go/ipa/106' | ||
| message: '{{error}} http://go/ipa/106' | ||
| severity: warn | ||
| given: '$.paths[*].post' | ||
| then: | ||
| function: 'createMethodShouldNotHaveQueryParameters' | ||
| xgen-IPA-106-create-method-request-body-is-get-method-response: | ||
| description: 'The Create method request should be a Get method response. http://go/ipa/106' | ||
| description: | | ||
|
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. ❤️ |
||
| Request body content of the Create method and response content of the Get method should refer to the same resource. | ||
| readOnly/writeOnly properties will be ignored. http://go/ipa/106 | ||
| message: '{{error}} http://go/ipa/106' | ||
| severity: warn | ||
| given: '$.paths[*].post.requestBody.content' | ||
| then: | ||
| field: '@key' | ||
| function: 'createMethodRequestBodyIsGetResponse' | ||
| functionOptions: | ||
| ignoredValues: ['readOnly', 'writeOnly'] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,14 @@ | ||
| import { getResponseOfGetMethodByMediaType, isCustomMethodIdentifier } from './utils/resourceEvaluation.js'; | ||
| import { resolveObject } from './utils/componentUtils.js'; | ||
| import { isEqual } from 'lodash'; | ||
| import omitDeep from 'omit-deep-lodash'; | ||
| import { isEqual, omitDeep } from './utils/compareUtils.js'; | ||
| import { hasException } from './utils/exceptions.js'; | ||
| import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js'; | ||
|
|
||
| const RULE_NAME = 'xgen-IPA-106-create-method-request-body-is-get-method-response'; | ||
| const ERROR_MESSAGE = | ||
| 'The request body schema properties must match the response body schema properties of the Get method.'; | ||
|
|
||
| export default (input, _, { path, documentInventory }) => { | ||
| export default (input, opts, { path, documentInventory }) => { | ||
| const oas = documentInventory.resolved; | ||
| const resourcePath = path[1]; | ||
| let mediaType = input; | ||
|
|
@@ -34,7 +33,8 @@ export default (input, _, { path, documentInventory }) => { | |
| const errors = checkViolationsAndReturnErrors( | ||
| path, | ||
| postMethodRequestContentPerMediaType, | ||
| getMethodResponseContentPerMediaType | ||
| getMethodResponseContentPerMediaType, | ||
| opts | ||
| ); | ||
|
|
||
| if (errors.length !== 0) { | ||
|
|
@@ -47,14 +47,16 @@ export default (input, _, { path, documentInventory }) => { | |
| function checkViolationsAndReturnErrors( | ||
| path, | ||
| postMethodRequestContentPerMediaType, | ||
| getMethodResponseContentPerMediaType | ||
| getMethodResponseContentPerMediaType, | ||
| opts | ||
| ) { | ||
| const errors = []; | ||
|
|
||
| const ignoredValues = opts?.ignoredValues || []; | ||
| if ( | ||
| !isEqual( | ||
| omitDeep(postMethodRequestContentPerMediaType.schema, 'readOnly', 'writeOnly'), | ||
| omitDeep(getMethodResponseContentPerMediaType.schema, 'readOnly', 'writeOnly') | ||
| omitDeep(postMethodRequestContentPerMediaType.schema, ...ignoredValues), | ||
|
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. ❤️ |
||
| omitDeep(getMethodResponseContentPerMediaType.schema, ...ignoredValues) | ||
| ) | ||
| ) { | ||
| errors.push({ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| // utils/compareUtils.js | ||
|
|
||
| /** | ||
| * Deep equality check between two values | ||
yelizhenden-mdb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| * @param {*} value1 First value to compare | ||
| * @param {*} value2 Second value to compare | ||
| * @returns {boolean} Whether the values are deeply equal | ||
| */ | ||
| export function isEqual(value1, value2) { | ||
yelizhenden-mdb marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // If the values are strictly equal (including handling null/undefined) | ||
| if (value1 === value2) return true; | ||
|
|
||
| // If either value is null or not an object, they're not equal (we already checked strict equality) | ||
| if (value1 == null || value2 == null || typeof value1 !== 'object' || typeof value2 !== 'object') { | ||
| return false; | ||
| } | ||
|
|
||
| const keys1 = Object.keys(value1); | ||
| const keys2 = Object.keys(value2); | ||
|
|
||
| // Different number of properties | ||
| if (keys1.length !== keys2.length) return false; | ||
|
|
||
| // Check that all properties in value1 exist in value2 and are equal | ||
| for (const key of keys1) { | ||
| if (!keys2.includes(key)) return false; | ||
|
|
||
| // Recursive equality check for nested objects | ||
| if (!isEqual(value1[key], value2[key])) return false; | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| /** | ||
| * Deep clone an object while omitting specific properties | ||
| * @param {object} obj Object to clone and omit properties from | ||
| * @param {...string} keys Properties to omit | ||
| * @returns {object} New object without the specified properties | ||
| */ | ||
| export function omitDeep(obj, ...keys) { | ||
| if (!obj || typeof obj !== 'object') return obj; | ||
|
|
||
| // Handle arrays | ||
| if (Array.isArray(obj)) { | ||
| return obj.map((item) => omitDeep(item, ...keys)); | ||
| } | ||
|
|
||
| // Handle regular objects | ||
| return Object.entries(obj).reduce((result, [key, value]) => { | ||
| // Skip properties that should be omitted | ||
| if (keys.includes(key)) return result; | ||
|
|
||
| // Handle nested objects/arrays recursively | ||
| if (value && typeof value === 'object') { | ||
| result[key] = omitDeep(value, ...keys); | ||
| } else { | ||
| result[key] = value; | ||
| } | ||
|
|
||
| return result; | ||
| }, {}); | ||
| } | ||
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.
❤️