From 6db3071828163c04927e6e09af8de9347ea1baa6 Mon Sep 17 00:00:00 2001 From: Yeliz Henden Date: Mon, 13 Jan 2025 18:19:24 +0000 Subject: [PATCH 01/13] CLOUDP-290417: [Product Metrics/Observability] Implement Collector class --- .../ipa/__tests__/utils/exceptions.test.js | 12 ++-- tools/spectral/ipa/metrics/Collector.js | 56 +++++++++++++++++++ .../eachCustomMethodMustBeGetOrPost.js | 7 ++- .../eachCustomMethodMustUseCamelCase.js | 7 ++- .../eachEnumValueMustBeUpperSnakeCase.js | 9 ++- ...ternatesBetweenResourceNameAndPathParam.js | 8 ++- .../functions/eachResourceHasGetMethod.js | 9 ++- .../functions/exceptionExtensionFormat.js | 9 +++ .../rulesets/functions/singletonHasNoId.js | 7 ++- .../rulesets/functions/utils/exceptions.js | 4 +- 10 files changed, 113 insertions(+), 15 deletions(-) create mode 100644 tools/spectral/ipa/metrics/Collector.js diff --git a/tools/spectral/ipa/__tests__/utils/exceptions.test.js b/tools/spectral/ipa/__tests__/utils/exceptions.test.js index 6646e861a6..1f140cbdf1 100644 --- a/tools/spectral/ipa/__tests__/utils/exceptions.test.js +++ b/tools/spectral/ipa/__tests__/utils/exceptions.test.js @@ -40,16 +40,16 @@ const objectWithIpa100And101Exception = { describe('tools/spectral/ipa/rulesets/functions/utils/exceptions.js', () => { describe('hasException', () => { it('returns true if object has exception matching the rule name', () => { - expect(hasException(objectWithIpa100Exception, TEST_RULE_NAME_100)).toBe(true); - expect(hasException(objectWithIpa100ExceptionAndOwnerExtension, TEST_RULE_NAME_100)).toBe(true); - expect(hasException(objectWithIpa100And101Exception, TEST_RULE_NAME_100)).toBe(true); + expect(hasException(objectWithIpa100Exception, TEST_RULE_NAME_100, '')).toBe(true); + expect(hasException(objectWithIpa100ExceptionAndOwnerExtension, TEST_RULE_NAME_100, '')).toBe(true); + expect(hasException(objectWithIpa100And101Exception, TEST_RULE_NAME_100, '')).toBe(true); }); it('returns false if object does not have exception matching the rule name', () => { - expect(hasException({}, TEST_RULE_NAME_100)).toBe(false); - expect(hasException(objectWithIpa101Exception, TEST_RULE_NAME_100)).toBe(false); + expect(hasException({}, TEST_RULE_NAME_100, '')).toBe(false); + expect(hasException(objectWithIpa101Exception, TEST_RULE_NAME_100, '')).toBe(false); }); it('returns false if object has nested exception matching the rule name', () => { - expect(hasException(objectWithNestedIpa100Exception, TEST_RULE_NAME_100)).toBe(false); + expect(hasException(objectWithNestedIpa100Exception, TEST_RULE_NAME_100, '')).toBe(false); }); }); }); diff --git a/tools/spectral/ipa/metrics/Collector.js b/tools/spectral/ipa/metrics/Collector.js new file mode 100644 index 0000000000..6529041929 --- /dev/null +++ b/tools/spectral/ipa/metrics/Collector.js @@ -0,0 +1,56 @@ +import * as fs from 'node:fs'; + +export const EntryType = Object.freeze({ + EXCEPTION: 'exceptions', + VIOLATION: 'violations', + ADOPTION: 'adoptions', +}); + +class Collector { + static instance; + + constructor() { + if (Collector.instance) { + return Collector.instance; + } + + this.entries = { + [EntryType.VIOLATION]: [], + [EntryType.ADOPTION]: [], + [EntryType.EXCEPTION]: [], + }; + this.fileName = "combined.log" + //this.exceptionFileName = 'exceptions.log'; + //this.violationFileName = 'violations.log'; + //this.adoptionFileName = 'adoptions.log'; + + + process.on('exit', () => this.flushToFile()); + process.on('SIGINT', () => { + this.flushToFile(); + process.exit(); // Ensure process exits on Ctrl+C + }); + + Collector.instance = this; + } + + add(componentId, ruleName, type) { + if(componentId && ruleName && type) { + componentId = componentId.join('.'); + const data = {componentId, ruleName}; + this.entries[type].push(data); + } + } + + flushToFile() { + try { + const data = JSON.stringify(this.entries, null, 2); + fs.writeFileSync(this.fileName, data); + } catch (error) { + console.error('Error writing exceptions to file:', error); + } + } +} + +const collector = new Collector(); +export default collector; diff --git a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js index fa14929c6f..0262ce2f50 100644 --- a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js +++ b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js @@ -1,5 +1,6 @@ import { isCustomMethod } from './utils/resourceEvaluation.js'; import { hasException } from './utils/exceptions.js'; +import collector, { EntryType } from '../../metrics/Collector.js'; const RULE_NAME = 'xgen-IPA-109-custom-method-must-be-GET-or-POST'; const ERROR_MESSAGE = 'The HTTP method for custom methods must be GET or POST.'; @@ -13,7 +14,7 @@ export default (input, opts, { path }) => { if (!isCustomMethod(pathKey)) return; - if (hasException(input, RULE_NAME)) { + if (hasException(input, RULE_NAME, path)) { return; } @@ -23,6 +24,7 @@ export default (input, opts, { path }) => { // Check for invalid methods if (httpMethods.some((method) => !VALID_METHODS.includes(method))) { + collector.add(path, RULE_NAME, EntryType.VIOLATION); return ERROR_RESULT; } @@ -30,6 +32,9 @@ export default (input, opts, { path }) => { const validMethodCount = httpMethods.filter((method) => VALID_METHODS.includes(method)).length; if (validMethodCount > 1) { + collector.add(path, RULE_NAME, EntryType.VIOLATION); return ERROR_RESULT; } + + collector.add(path, RULE_NAME, EntryType.ADOPTION); }; diff --git a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js index d027d78f67..07bdd02bf5 100644 --- a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js +++ b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js @@ -1,6 +1,7 @@ import { getCustomMethodName, isCustomMethod } from './utils/resourceEvaluation.js'; import { hasException } from './utils/exceptions.js'; import { casing } from '@stoplight/spectral-functions'; +import collector, { EntryType } from '../../metrics/Collector.js'; const RULE_NAME = 'xgen-IPA-109-custom-method-must-use-camel-case'; @@ -10,16 +11,20 @@ export default (input, opts, { path }) => { if (!isCustomMethod(pathKey)) return; - if (hasException(input, RULE_NAME)) { + if (hasException(input, RULE_NAME, path)) { return; } let methodName = getCustomMethodName(pathKey); if (methodName.length === 0 || methodName.trim().length === 0) { + collector.add(path, RULE_NAME, EntryType.VIOLATION); return [{ message: 'Custom method name cannot be empty or blank.' }]; } if (casing(methodName, { type: 'camel', disallowDigits: true })) { + collector.add(path, RULE_NAME, EntryType.VIOLATION); return [{ message: `${methodName} must use camelCase format.` }]; } + + collector.add(path, RULE_NAME, EntryType.ADOPTION); }; diff --git a/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js b/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js index 39c05ae5d0..fbfa87955c 100644 --- a/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js +++ b/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js @@ -1,6 +1,7 @@ import { hasException } from './utils/exceptions.js'; import { resolveObject } from './utils/componentUtils.js'; import { casing } from '@stoplight/spectral-functions'; +import collector, { EntryType } from '../../metrics/Collector.js'; const RULE_NAME = 'xgen-IPA-123-enum-values-must-be-upper-snake-case'; const ERROR_MESSAGE = 'enum value must be UPPER_SNAKE_CASE.'; @@ -17,7 +18,7 @@ export default (input, _, { path, documentInventory }) => { const oas = documentInventory.resolved; const schemaPath = getSchemaPathFromEnumPath(path); const schemaObject = resolveObject(oas, schemaPath); - if (hasException(schemaObject, RULE_NAME)) { + if (hasException(schemaObject, RULE_NAME, schemaPath)) { return; } @@ -33,5 +34,11 @@ export default (input, _, { path, documentInventory }) => { } }); + if(errors.length === 0) { + collector.add(path, RULE_NAME, EntryType.ADOPTION); + } else { + collector.add(path, RULE_NAME, EntryType.VIOLATION); + } + return errors; }; diff --git a/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js b/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js index eb418aae61..abd5acf38e 100644 --- a/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js +++ b/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js @@ -1,5 +1,6 @@ import { isPathParam } from './utils/componentUtils.js'; import { hasException } from './utils/exceptions.js'; +import collector, { EntryType } from '../../metrics/Collector.js'; const RULE_NAME = 'xgen-IPA-102-path-alternate-resource-name-path-param'; const ERROR_MESSAGE = 'API paths must alternate between resource name and path params.'; @@ -24,9 +25,9 @@ const validatePathStructure = (elements) => { }); }; -export default (input, _, { documentInventory }) => { +export default (input, _, { path, documentInventory }) => { const oas = documentInventory.resolved; - if (hasException(oas.paths[input], RULE_NAME)) { + if (hasException(oas.paths[input], RULE_NAME, path)) { return; } @@ -43,6 +44,9 @@ export default (input, _, { documentInventory }) => { let suffix = suffixWithLeadingSlash.slice(1); let elements = suffix.split('/'); if (!validatePathStructure(elements)) { + collector.add(path, RULE_NAME, EntryType.VIOLATION); return ERROR_RESULT; } + + collector.add(path, RULE_NAME, EntryType.ADOPTION); }; diff --git a/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js b/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js index 840b18320d..e3a3fec210 100644 --- a/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js +++ b/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js @@ -7,18 +7,19 @@ import { getResourcePaths, } from './utils/resourceEvaluation.js'; import { hasException } from './utils/exceptions.js'; +import collector, { EntryType } from '../../metrics/Collector.js'; const RULE_NAME = 'xgen-IPA-104-resource-has-GET'; const ERROR_MESSAGE = 'APIs must provide a get method for resources.'; -export default (input, _, { documentInventory }) => { +export default (input, _, { path, documentInventory }) => { if (isChild(input) || isCustomMethod(input)) { return; } const oas = documentInventory.resolved; - if (hasException(oas.paths[input], RULE_NAME)) { + if (hasException(oas.paths[input], RULE_NAME, path)) { return; } @@ -26,6 +27,7 @@ export default (input, _, { documentInventory }) => { if (isSingletonResource(resourcePaths)) { if (!hasGetMethod(oas.paths[resourcePaths[0]])) { + collector.add(path, RULE_NAME, EntryType.VIOLATION); return [ { message: ERROR_MESSAGE, @@ -34,6 +36,7 @@ export default (input, _, { documentInventory }) => { } } else if (isStandardResource(resourcePaths)) { if (!hasGetMethod(oas.paths[resourcePaths[1]])) { + collector.add(path, RULE_NAME, EntryType.VIOLATION); return [ { message: ERROR_MESSAGE, @@ -41,4 +44,6 @@ export default (input, _, { documentInventory }) => { ]; } } + + collector.add(path, RULE_NAME, EntryType.ADOPTION); }; diff --git a/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js b/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js index 0cb71e7c56..be0c60f730 100644 --- a/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js +++ b/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js @@ -1,3 +1,6 @@ +import collector, { EntryType } from '../../metrics/Collector.js'; + +const RULE_NAME = 'xgen-IPA-005-exception-extension-format'; const ERROR_MESSAGE = 'IPA exceptions must have a valid rule name and a reason.'; const RULE_NAME_PREFIX = 'xgen-IPA-'; @@ -16,6 +19,12 @@ export default (input, _, { path }) => { } }); + if(errors.length === 0) { + collector.add(path, RULE_NAME, EntryType.ADOPTION); + } else { + collector.add(path, RULE_NAME, EntryType.VIOLATION); + } + return errors; }; diff --git a/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js b/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js index 038514c95a..64d868c303 100644 --- a/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js +++ b/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js @@ -7,6 +7,7 @@ import { } from './utils/resourceEvaluation.js'; import { hasException } from './utils/exceptions.js'; import { getAllSuccessfulGetResponseSchemas } from './utils/methodUtils.js'; +import collector, { EntryType } from '../../metrics/Collector.js'; const RULE_NAME = 'xgen-IPA-113-singleton-must-not-have-id'; const ERROR_MESSAGE = 'Singleton resources must not have a user-provided or system-generated ID.'; @@ -18,7 +19,7 @@ export default (input, opts, { path, documentInventory }) => { return; } - if (hasException(input, RULE_NAME)) { + if (hasException(input, RULE_NAME, path)) { return; } @@ -28,6 +29,7 @@ export default (input, opts, { path, documentInventory }) => { if (isSingletonResource(resourcePaths) && hasGetMethod(input)) { const resourceSchemas = getAllSuccessfulGetResponseSchemas(input); if (resourceSchemas.some((schema) => schemaHasIdProperty(schema))) { + collector.add(path, RULE_NAME, EntryType.VIOLATION); return [ { message: ERROR_MESSAGE, @@ -35,6 +37,9 @@ export default (input, opts, { path, documentInventory }) => { ]; } } + + collector.add(path, RULE_NAME, EntryType.ADOPTION); + }; function schemaHasIdProperty(schema) { diff --git a/tools/spectral/ipa/rulesets/functions/utils/exceptions.js b/tools/spectral/ipa/rulesets/functions/utils/exceptions.js index 91b6f84f68..8af9459c6e 100644 --- a/tools/spectral/ipa/rulesets/functions/utils/exceptions.js +++ b/tools/spectral/ipa/rulesets/functions/utils/exceptions.js @@ -1,3 +1,4 @@ +import collector, { EntryType } from '../../../metrics/Collector.js'; const EXCEPTION_EXTENSION = 'x-xgen-IPA-exception'; /** @@ -7,8 +8,9 @@ const EXCEPTION_EXTENSION = 'x-xgen-IPA-exception'; * @param ruleName the name of the exempted rule * @returns {boolean} true if the object has an exception named ruleName, otherwise false */ -export function hasException(object, ruleName) { +export function hasException(object, ruleName, path) { if (object[EXCEPTION_EXTENSION]) { + collector.add(path, ruleName, EntryType.EXCEPTION) return Object.keys(object[EXCEPTION_EXTENSION]).includes(ruleName); } return false; From e6c1b06604230b88672ec798ecdc330113b17ff1 Mon Sep 17 00:00:00 2001 From: Yeliz Henden Date: Tue, 14 Jan 2025 08:41:49 +0000 Subject: [PATCH 02/13] Fix collector class singleton pattern --- .../ipa/__tests__/metrics/collector.test.js | 60 +++++++++++++++++++ .../metrics/{Collector.js => collector.js} | 22 +++---- .../eachCustomMethodMustBeGetOrPost.js | 2 +- .../eachCustomMethodMustUseCamelCase.js | 2 +- .../eachEnumValueMustBeUpperSnakeCase.js | 6 +- ...ternatesBetweenResourceNameAndPathParam.js | 2 +- .../functions/eachResourceHasGetMethod.js | 2 +- .../functions/exceptionExtensionFormat.js | 2 +- .../rulesets/functions/singletonHasNoId.js | 2 +- .../rulesets/functions/utils/exceptions.js | 2 +- 10 files changed, 82 insertions(+), 20 deletions(-) create mode 100644 tools/spectral/ipa/__tests__/metrics/collector.test.js rename tools/spectral/ipa/metrics/{Collector.js => collector.js} (75%) diff --git a/tools/spectral/ipa/__tests__/metrics/collector.test.js b/tools/spectral/ipa/__tests__/metrics/collector.test.js new file mode 100644 index 0000000000..f66d811052 --- /dev/null +++ b/tools/spectral/ipa/__tests__/metrics/collector.test.js @@ -0,0 +1,60 @@ +import { beforeEach, describe, expect, it } from '@jest/globals'; +import collector, { EntryType } from '../../metrics/collector'; +import * as fs from 'node:fs'; + +jest.mock('node:fs'); + +describe('Collector Class', () => { + const expectedOutput = { + violations: [ + { componentId: 'example.component', ruleName: 'rule-1' }, + { componentId: 'example.component', ruleName: 'rule-2' }, + ], + adoptions: [ + { componentId: 'example.component', ruleName: 'rule-3' }, + ], + exceptions: [ + { componentId: 'example.component', ruleName: 'rule-4' }, + ], + }; + + beforeEach(() => { + collector.entries = { + [EntryType.VIOLATION]: [], + [EntryType.ADOPTION]: [], + [EntryType.EXCEPTION]: [], + }; + + jest.clearAllMocks(); + }); + + it('should collect violations, adoptions, and exceptions correctly', () => { + collector.add(['example', 'component'], 'rule-1', EntryType.VIOLATION); + collector.add(['example', 'component'], 'rule-2', EntryType.VIOLATION); + collector.add(['example', 'component'], 'rule-3', EntryType.ADOPTION); + collector.add(['example', 'component'], 'rule-4', EntryType.EXCEPTION); + + expect(collector.entries).toEqual(expectedOutput); + + collector.flushToFile(); + const writtenData = JSON.stringify(expectedOutput, null, 2); + expect(fs.writeFileSync).toHaveBeenCalledWith( + 'combined.log', + writtenData + ); + }); + + it('should not add invalid entries', () => { + collector.add(null, 'rule-1', EntryType.VIOLATION); + collector.add(['example', 'component'], null, EntryType.ADOPTION); + collector.add(['example', 'component'], 'rule-4', null); + + expect(collector.entries).toEqual({ + violations: [], + adoptions: [], + exceptions: [], + }); + + expect(fs.writeFileSync).not.toHaveBeenCalled(); + }); +}); \ No newline at end of file diff --git a/tools/spectral/ipa/metrics/Collector.js b/tools/spectral/ipa/metrics/collector.js similarity index 75% rename from tools/spectral/ipa/metrics/Collector.js rename to tools/spectral/ipa/metrics/collector.js index 6529041929..c3b3ce866a 100644 --- a/tools/spectral/ipa/metrics/Collector.js +++ b/tools/spectral/ipa/metrics/collector.js @@ -7,11 +7,18 @@ export const EntryType = Object.freeze({ }); class Collector { - static instance; + static instance = null; + + static getInstance() { + if (!this.instance) { + this.instance = new Collector(); + } + return this.instance; + } constructor() { if (Collector.instance) { - return Collector.instance; + throw new Error('Use Collector.getInstance()'); } this.entries = { @@ -19,19 +26,14 @@ class Collector { [EntryType.ADOPTION]: [], [EntryType.EXCEPTION]: [], }; - this.fileName = "combined.log" - //this.exceptionFileName = 'exceptions.log'; - //this.violationFileName = 'violations.log'; - //this.adoptionFileName = 'adoptions.log'; + this.fileName = "combined.log" process.on('exit', () => this.flushToFile()); process.on('SIGINT', () => { this.flushToFile(); - process.exit(); // Ensure process exits on Ctrl+C + process.exit(); }); - - Collector.instance = this; } add(componentId, ruleName, type) { @@ -52,5 +54,5 @@ class Collector { } } -const collector = new Collector(); +const collector = Collector.getInstance(); export default collector; diff --git a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js index 0262ce2f50..dcfc185fdd 100644 --- a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js +++ b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js @@ -1,6 +1,6 @@ import { isCustomMethod } from './utils/resourceEvaluation.js'; import { hasException } from './utils/exceptions.js'; -import collector, { EntryType } from '../../metrics/Collector.js'; +import collector, { EntryType } from '../../metrics/collector.js'; const RULE_NAME = 'xgen-IPA-109-custom-method-must-be-GET-or-POST'; const ERROR_MESSAGE = 'The HTTP method for custom methods must be GET or POST.'; diff --git a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js index 07bdd02bf5..88d91e7230 100644 --- a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js +++ b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js @@ -1,7 +1,7 @@ import { getCustomMethodName, isCustomMethod } from './utils/resourceEvaluation.js'; import { hasException } from './utils/exceptions.js'; import { casing } from '@stoplight/spectral-functions'; -import collector, { EntryType } from '../../metrics/Collector.js'; +import collector, { EntryType } from '../../metrics/collector.js'; const RULE_NAME = 'xgen-IPA-109-custom-method-must-use-camel-case'; diff --git a/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js b/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js index fbfa87955c..060b713378 100644 --- a/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js +++ b/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js @@ -1,7 +1,7 @@ import { hasException } from './utils/exceptions.js'; import { resolveObject } from './utils/componentUtils.js'; import { casing } from '@stoplight/spectral-functions'; -import collector, { EntryType } from '../../metrics/Collector.js'; +import collector, { EntryType } from '../../metrics/collector.js'; const RULE_NAME = 'xgen-IPA-123-enum-values-must-be-upper-snake-case'; const ERROR_MESSAGE = 'enum value must be UPPER_SNAKE_CASE.'; @@ -35,9 +35,9 @@ export default (input, _, { path, documentInventory }) => { }); if(errors.length === 0) { - collector.add(path, RULE_NAME, EntryType.ADOPTION); + collector.add(schemaPath, RULE_NAME, EntryType.ADOPTION); } else { - collector.add(path, RULE_NAME, EntryType.VIOLATION); + collector.add(schemaPath, RULE_NAME, EntryType.VIOLATION); } return errors; diff --git a/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js b/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js index abd5acf38e..984f742bbf 100644 --- a/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js +++ b/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js @@ -1,6 +1,6 @@ import { isPathParam } from './utils/componentUtils.js'; import { hasException } from './utils/exceptions.js'; -import collector, { EntryType } from '../../metrics/Collector.js'; +import collector, { EntryType } from '../../metrics/collector.js'; const RULE_NAME = 'xgen-IPA-102-path-alternate-resource-name-path-param'; const ERROR_MESSAGE = 'API paths must alternate between resource name and path params.'; diff --git a/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js b/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js index e3a3fec210..936a688f20 100644 --- a/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js +++ b/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js @@ -7,7 +7,7 @@ import { getResourcePaths, } from './utils/resourceEvaluation.js'; import { hasException } from './utils/exceptions.js'; -import collector, { EntryType } from '../../metrics/Collector.js'; +import collector, { EntryType } from '../../metrics/collector.js'; const RULE_NAME = 'xgen-IPA-104-resource-has-GET'; const ERROR_MESSAGE = 'APIs must provide a get method for resources.'; diff --git a/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js b/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js index be0c60f730..00af3fa31e 100644 --- a/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js +++ b/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js @@ -1,4 +1,4 @@ -import collector, { EntryType } from '../../metrics/Collector.js'; +import collector, { EntryType } from '../../metrics/collector.js'; const RULE_NAME = 'xgen-IPA-005-exception-extension-format'; const ERROR_MESSAGE = 'IPA exceptions must have a valid rule name and a reason.'; diff --git a/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js b/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js index 64d868c303..0ae8c74e17 100644 --- a/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js +++ b/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js @@ -7,7 +7,7 @@ import { } from './utils/resourceEvaluation.js'; import { hasException } from './utils/exceptions.js'; import { getAllSuccessfulGetResponseSchemas } from './utils/methodUtils.js'; -import collector, { EntryType } from '../../metrics/Collector.js'; +import collector, { EntryType } from '../../metrics/collector.js'; const RULE_NAME = 'xgen-IPA-113-singleton-must-not-have-id'; const ERROR_MESSAGE = 'Singleton resources must not have a user-provided or system-generated ID.'; diff --git a/tools/spectral/ipa/rulesets/functions/utils/exceptions.js b/tools/spectral/ipa/rulesets/functions/utils/exceptions.js index 8af9459c6e..f7f6001255 100644 --- a/tools/spectral/ipa/rulesets/functions/utils/exceptions.js +++ b/tools/spectral/ipa/rulesets/functions/utils/exceptions.js @@ -1,4 +1,4 @@ -import collector, { EntryType } from '../../../metrics/Collector.js'; +import collector, { EntryType } from '../../../metrics/collector.js'; const EXCEPTION_EXTENSION = 'x-xgen-IPA-exception'; /** From daaaa87b924bde1f7fca9e3a59503d92ecb0240b Mon Sep 17 00:00:00 2001 From: Yeliz Henden Date: Tue, 14 Jan 2025 08:43:03 +0000 Subject: [PATCH 03/13] Prettier fix --- .../ipa/__tests__/metrics/collector.test.js | 15 ++++----------- tools/spectral/ipa/metrics/collector.js | 6 +++--- .../eachEnumValueMustBeUpperSnakeCase.js | 2 +- .../functions/exceptionExtensionFormat.js | 2 +- .../ipa/rulesets/functions/singletonHasNoId.js | 1 - .../ipa/rulesets/functions/utils/exceptions.js | 2 +- 6 files changed, 10 insertions(+), 18 deletions(-) diff --git a/tools/spectral/ipa/__tests__/metrics/collector.test.js b/tools/spectral/ipa/__tests__/metrics/collector.test.js index f66d811052..adb7378468 100644 --- a/tools/spectral/ipa/__tests__/metrics/collector.test.js +++ b/tools/spectral/ipa/__tests__/metrics/collector.test.js @@ -10,12 +10,8 @@ describe('Collector Class', () => { { componentId: 'example.component', ruleName: 'rule-1' }, { componentId: 'example.component', ruleName: 'rule-2' }, ], - adoptions: [ - { componentId: 'example.component', ruleName: 'rule-3' }, - ], - exceptions: [ - { componentId: 'example.component', ruleName: 'rule-4' }, - ], + adoptions: [{ componentId: 'example.component', ruleName: 'rule-3' }], + exceptions: [{ componentId: 'example.component', ruleName: 'rule-4' }], }; beforeEach(() => { @@ -38,10 +34,7 @@ describe('Collector Class', () => { collector.flushToFile(); const writtenData = JSON.stringify(expectedOutput, null, 2); - expect(fs.writeFileSync).toHaveBeenCalledWith( - 'combined.log', - writtenData - ); + expect(fs.writeFileSync).toHaveBeenCalledWith('combined.log', writtenData); }); it('should not add invalid entries', () => { @@ -57,4 +50,4 @@ describe('Collector Class', () => { expect(fs.writeFileSync).not.toHaveBeenCalled(); }); -}); \ No newline at end of file +}); diff --git a/tools/spectral/ipa/metrics/collector.js b/tools/spectral/ipa/metrics/collector.js index c3b3ce866a..a92954c693 100644 --- a/tools/spectral/ipa/metrics/collector.js +++ b/tools/spectral/ipa/metrics/collector.js @@ -27,7 +27,7 @@ class Collector { [EntryType.EXCEPTION]: [], }; - this.fileName = "combined.log" + this.fileName = 'combined.log'; process.on('exit', () => this.flushToFile()); process.on('SIGINT', () => { @@ -37,9 +37,9 @@ class Collector { } add(componentId, ruleName, type) { - if(componentId && ruleName && type) { + if (componentId && ruleName && type) { componentId = componentId.join('.'); - const data = {componentId, ruleName}; + const data = { componentId, ruleName }; this.entries[type].push(data); } } diff --git a/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js b/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js index 060b713378..59fcd226ab 100644 --- a/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js +++ b/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js @@ -34,7 +34,7 @@ export default (input, _, { path, documentInventory }) => { } }); - if(errors.length === 0) { + if (errors.length === 0) { collector.add(schemaPath, RULE_NAME, EntryType.ADOPTION); } else { collector.add(schemaPath, RULE_NAME, EntryType.VIOLATION); diff --git a/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js b/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js index 00af3fa31e..aae9971b01 100644 --- a/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js +++ b/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js @@ -19,7 +19,7 @@ export default (input, _, { path }) => { } }); - if(errors.length === 0) { + if (errors.length === 0) { collector.add(path, RULE_NAME, EntryType.ADOPTION); } else { collector.add(path, RULE_NAME, EntryType.VIOLATION); diff --git a/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js b/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js index 0ae8c74e17..5aad183fdb 100644 --- a/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js +++ b/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js @@ -39,7 +39,6 @@ export default (input, opts, { path, documentInventory }) => { } collector.add(path, RULE_NAME, EntryType.ADOPTION); - }; function schemaHasIdProperty(schema) { diff --git a/tools/spectral/ipa/rulesets/functions/utils/exceptions.js b/tools/spectral/ipa/rulesets/functions/utils/exceptions.js index f7f6001255..c3f9ec1ecc 100644 --- a/tools/spectral/ipa/rulesets/functions/utils/exceptions.js +++ b/tools/spectral/ipa/rulesets/functions/utils/exceptions.js @@ -10,7 +10,7 @@ const EXCEPTION_EXTENSION = 'x-xgen-IPA-exception'; */ export function hasException(object, ruleName, path) { if (object[EXCEPTION_EXTENSION]) { - collector.add(path, ruleName, EntryType.EXCEPTION) + collector.add(path, ruleName, EntryType.EXCEPTION); return Object.keys(object[EXCEPTION_EXTENSION]).includes(ruleName); } return false; From fa6362c7b191c6dd94c5c0b6e353a70f7cc0d88f Mon Sep 17 00:00:00 2001 From: Yeliz Henden Date: Tue, 14 Jan 2025 14:09:28 +0000 Subject: [PATCH 04/13] Fix: Include exception reason --- .../ipa/__tests__/metrics/collector.test.js | 12 ++++++------ tools/spectral/ipa/metrics/collector.js | 17 +++++++++++++---- .../eachCustomMethodMustBeGetOrPost.js | 11 ++++++----- .../eachCustomMethodMustUseCamelCase.js | 6 +++--- .../eachEnumValueMustBeUpperSnakeCase.js | 9 +++++---- ...lternatesBetweenResourceNameAndPathParam.js | 9 +++++---- .../functions/eachResourceHasGetMethod.js | 6 +++--- .../functions/exceptionExtensionFormat.js | 4 ++-- .../ipa/rulesets/functions/singletonHasNoId.js | 9 +++++---- .../ipa/rulesets/functions/utils/exceptions.js | 18 ++++++++++++++++-- 10 files changed, 64 insertions(+), 37 deletions(-) diff --git a/tools/spectral/ipa/__tests__/metrics/collector.test.js b/tools/spectral/ipa/__tests__/metrics/collector.test.js index adb7378468..c11b8c49f6 100644 --- a/tools/spectral/ipa/__tests__/metrics/collector.test.js +++ b/tools/spectral/ipa/__tests__/metrics/collector.test.js @@ -11,7 +11,7 @@ describe('Collector Class', () => { { componentId: 'example.component', ruleName: 'rule-2' }, ], adoptions: [{ componentId: 'example.component', ruleName: 'rule-3' }], - exceptions: [{ componentId: 'example.component', ruleName: 'rule-4' }], + exceptions: [{ componentId: 'example.component', ruleName: 'rule-4', exceptionReason: 'exception-reason' }], }; beforeEach(() => { @@ -25,16 +25,16 @@ describe('Collector Class', () => { }); it('should collect violations, adoptions, and exceptions correctly', () => { - collector.add(['example', 'component'], 'rule-1', EntryType.VIOLATION); - collector.add(['example', 'component'], 'rule-2', EntryType.VIOLATION); - collector.add(['example', 'component'], 'rule-3', EntryType.ADOPTION); - collector.add(['example', 'component'], 'rule-4', EntryType.EXCEPTION); + collector.add(EntryType.VIOLATION, ['example', 'component'], 'rule-1'); + collector.add(EntryType.VIOLATION, ['example', 'component'], 'rule-2'); + collector.add(EntryType.ADOPTION, ['example', 'component'], 'rule-3'); + collector.add(EntryType.EXCEPTION, ['example', 'component'], 'rule-4', 'exception-reason'); expect(collector.entries).toEqual(expectedOutput); collector.flushToFile(); const writtenData = JSON.stringify(expectedOutput, null, 2); - expect(fs.writeFileSync).toHaveBeenCalledWith('combined.log', writtenData); + expect(fs.writeFileSync).toHaveBeenCalledWith('ipa-collector-results-combined.log', writtenData); }); it('should not add invalid entries', () => { diff --git a/tools/spectral/ipa/metrics/collector.js b/tools/spectral/ipa/metrics/collector.js index a92954c693..cc92b96d40 100644 --- a/tools/spectral/ipa/metrics/collector.js +++ b/tools/spectral/ipa/metrics/collector.js @@ -27,7 +27,7 @@ class Collector { [EntryType.EXCEPTION]: [], }; - this.fileName = 'combined.log'; + this.fileName = 'ipa-collector-results-combined.log'; process.on('exit', () => this.flushToFile()); process.on('SIGINT', () => { @@ -36,11 +36,20 @@ class Collector { }); } - add(componentId, ruleName, type) { + add(type, componentId, ruleName, exceptionReason = null ) { if (componentId && ruleName && type) { + if (!Object.values(EntryType).includes(type)) { + throw new Error(`Invalid entry type: ${type}`); + } + componentId = componentId.join('.'); - const data = { componentId, ruleName }; - this.entries[type].push(data); + const entry = {componentId, ruleName}; + + if(type === EntryType.EXCEPTION && exceptionReason) { + entry.exceptionReason = exceptionReason; + } + + this.entries[type].push(entry); } } diff --git a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js index dcfc185fdd..c7d638b8a3 100644 --- a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js +++ b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js @@ -1,5 +1,5 @@ import { isCustomMethod } from './utils/resourceEvaluation.js'; -import { hasException } from './utils/exceptions.js'; +import { collectException, hasException } from './utils/exceptions.js'; import collector, { EntryType } from '../../metrics/collector.js'; const RULE_NAME = 'xgen-IPA-109-custom-method-must-be-GET-or-POST'; @@ -14,7 +14,8 @@ export default (input, opts, { path }) => { if (!isCustomMethod(pathKey)) return; - if (hasException(input, RULE_NAME, path)) { + if (hasException(input, RULE_NAME)) { + collectException(input, RULE_NAME, path); return; } @@ -24,7 +25,7 @@ export default (input, opts, { path }) => { // Check for invalid methods if (httpMethods.some((method) => !VALID_METHODS.includes(method))) { - collector.add(path, RULE_NAME, EntryType.VIOLATION); + collector.add(EntryType.VIOLATION, path, RULE_NAME); return ERROR_RESULT; } @@ -32,9 +33,9 @@ export default (input, opts, { path }) => { const validMethodCount = httpMethods.filter((method) => VALID_METHODS.includes(method)).length; if (validMethodCount > 1) { - collector.add(path, RULE_NAME, EntryType.VIOLATION); + collector.add(EntryType.VIOLATION, path, RULE_NAME); return ERROR_RESULT; } - collector.add(path, RULE_NAME, EntryType.ADOPTION); + collector.add(EntryType.ADOPTION, path, RULE_NAME); }; diff --git a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js index 88d91e7230..80656bb19e 100644 --- a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js +++ b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js @@ -17,14 +17,14 @@ export default (input, opts, { path }) => { let methodName = getCustomMethodName(pathKey); if (methodName.length === 0 || methodName.trim().length === 0) { - collector.add(path, RULE_NAME, EntryType.VIOLATION); + collector.add(EntryType.VIOLATION, path, RULE_NAME); return [{ message: 'Custom method name cannot be empty or blank.' }]; } if (casing(methodName, { type: 'camel', disallowDigits: true })) { - collector.add(path, RULE_NAME, EntryType.VIOLATION); + collector.add(EntryType.VIOLATION, path, RULE_NAME); return [{ message: `${methodName} must use camelCase format.` }]; } - collector.add(path, RULE_NAME, EntryType.ADOPTION); + collector.add(EntryType.ADOPTION, path, RULE_NAME); }; diff --git a/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js b/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js index 59fcd226ab..980e39641f 100644 --- a/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js +++ b/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js @@ -1,4 +1,4 @@ -import { hasException } from './utils/exceptions.js'; +import { collectException, hasException } from './utils/exceptions.js'; import { resolveObject } from './utils/componentUtils.js'; import { casing } from '@stoplight/spectral-functions'; import collector, { EntryType } from '../../metrics/collector.js'; @@ -18,7 +18,8 @@ export default (input, _, { path, documentInventory }) => { const oas = documentInventory.resolved; const schemaPath = getSchemaPathFromEnumPath(path); const schemaObject = resolveObject(oas, schemaPath); - if (hasException(schemaObject, RULE_NAME, schemaPath)) { + if (hasException(schemaObject, RULE_NAME)) { + collectException(schemaObject, RULE_NAME, path); return; } @@ -35,9 +36,9 @@ export default (input, _, { path, documentInventory }) => { }); if (errors.length === 0) { - collector.add(schemaPath, RULE_NAME, EntryType.ADOPTION); + collector.add(EntryType.ADOPTION, schemaPath, RULE_NAME); } else { - collector.add(schemaPath, RULE_NAME, EntryType.VIOLATION); + collector.add(EntryType.VIOLATION, schemaPath, RULE_NAME); } return errors; diff --git a/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js b/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js index 984f742bbf..7e1b550deb 100644 --- a/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js +++ b/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js @@ -1,5 +1,5 @@ import { isPathParam } from './utils/componentUtils.js'; -import { hasException } from './utils/exceptions.js'; +import { collectException, hasException } from './utils/exceptions.js'; import collector, { EntryType } from '../../metrics/collector.js'; const RULE_NAME = 'xgen-IPA-102-path-alternate-resource-name-path-param'; @@ -27,7 +27,8 @@ const validatePathStructure = (elements) => { export default (input, _, { path, documentInventory }) => { const oas = documentInventory.resolved; - if (hasException(oas.paths[input], RULE_NAME, path)) { + if (hasException(oas.paths[input], RULE_NAME)) { + collectException(oas.paths[input], RULE_NAME, path); return; } @@ -44,9 +45,9 @@ export default (input, _, { path, documentInventory }) => { let suffix = suffixWithLeadingSlash.slice(1); let elements = suffix.split('/'); if (!validatePathStructure(elements)) { - collector.add(path, RULE_NAME, EntryType.VIOLATION); + collector.add(EntryType.VIOLATION, path, RULE_NAME); return ERROR_RESULT; } - collector.add(path, RULE_NAME, EntryType.ADOPTION); + collector.add(EntryType.ADOPTION, path, RULE_NAME); }; diff --git a/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js b/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js index 936a688f20..95138ed55f 100644 --- a/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js +++ b/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js @@ -27,7 +27,7 @@ export default (input, _, { path, documentInventory }) => { if (isSingletonResource(resourcePaths)) { if (!hasGetMethod(oas.paths[resourcePaths[0]])) { - collector.add(path, RULE_NAME, EntryType.VIOLATION); + collector.add(EntryType.VIOLATION, path, RULE_NAME); return [ { message: ERROR_MESSAGE, @@ -36,7 +36,7 @@ export default (input, _, { path, documentInventory }) => { } } else if (isStandardResource(resourcePaths)) { if (!hasGetMethod(oas.paths[resourcePaths[1]])) { - collector.add(path, RULE_NAME, EntryType.VIOLATION); + collector.add(EntryType.VIOLATION, path, RULE_NAME); return [ { message: ERROR_MESSAGE, @@ -45,5 +45,5 @@ export default (input, _, { path, documentInventory }) => { } } - collector.add(path, RULE_NAME, EntryType.ADOPTION); + collector.add(EntryType.ADOPTION, path, RULE_NAME); }; diff --git a/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js b/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js index aae9971b01..46ba282803 100644 --- a/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js +++ b/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js @@ -20,9 +20,9 @@ export default (input, _, { path }) => { }); if (errors.length === 0) { - collector.add(path, RULE_NAME, EntryType.ADOPTION); + collector.add(EntryType.ADOPTION, path, RULE_NAME); } else { - collector.add(path, RULE_NAME, EntryType.VIOLATION); + collector.add(EntryType.VIOLATION, path, RULE_NAME); } return errors; diff --git a/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js b/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js index 5aad183fdb..9fcccd3ea2 100644 --- a/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js +++ b/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js @@ -5,7 +5,7 @@ import { isCustomMethod, isSingletonResource, } from './utils/resourceEvaluation.js'; -import { hasException } from './utils/exceptions.js'; +import { collectException, hasException } from './utils/exceptions.js'; import { getAllSuccessfulGetResponseSchemas } from './utils/methodUtils.js'; import collector, { EntryType } from '../../metrics/collector.js'; @@ -19,7 +19,8 @@ export default (input, opts, { path, documentInventory }) => { return; } - if (hasException(input, RULE_NAME, path)) { + if (hasException(input, RULE_NAME)) { + collectException(input, RULE_NAME, path) return; } @@ -29,7 +30,7 @@ export default (input, opts, { path, documentInventory }) => { if (isSingletonResource(resourcePaths) && hasGetMethod(input)) { const resourceSchemas = getAllSuccessfulGetResponseSchemas(input); if (resourceSchemas.some((schema) => schemaHasIdProperty(schema))) { - collector.add(path, RULE_NAME, EntryType.VIOLATION); + collector.add(EntryType.VIOLATION, path, RULE_NAME); return [ { message: ERROR_MESSAGE, @@ -38,7 +39,7 @@ export default (input, opts, { path, documentInventory }) => { } } - collector.add(path, RULE_NAME, EntryType.ADOPTION); + collector.add(EntryType.ADOPTION, path, RULE_NAME); }; function schemaHasIdProperty(schema) { diff --git a/tools/spectral/ipa/rulesets/functions/utils/exceptions.js b/tools/spectral/ipa/rulesets/functions/utils/exceptions.js index c3f9ec1ecc..244c11e08a 100644 --- a/tools/spectral/ipa/rulesets/functions/utils/exceptions.js +++ b/tools/spectral/ipa/rulesets/functions/utils/exceptions.js @@ -1,4 +1,5 @@ import collector, { EntryType } from '../../../metrics/collector.js'; + const EXCEPTION_EXTENSION = 'x-xgen-IPA-exception'; /** @@ -8,10 +9,23 @@ const EXCEPTION_EXTENSION = 'x-xgen-IPA-exception'; * @param ruleName the name of the exempted rule * @returns {boolean} true if the object has an exception named ruleName, otherwise false */ -export function hasException(object, ruleName, path) { +export function hasException(object, ruleName) { if (object[EXCEPTION_EXTENSION]) { - collector.add(path, ruleName, EntryType.EXCEPTION); return Object.keys(object[EXCEPTION_EXTENSION]).includes(ruleName); } return false; } + +/** + * Collects the information about the object if the object has an exception defined for the given rule + * + * @param object the object to evaluate + * @param ruleName the name of the exempted rule + * @param path the JSON path to the object + */ +export function collectException(object, ruleName, path) { + let exceptionReason = object[EXCEPTION_EXTENSION][ruleName]; + if(exceptionReason) { + collector.add(EntryType.EXCEPTION, path, ruleName, exceptionReason); + } +} From 0fca929aa1ddd695651ea53b8441b4c0afb9ea97 Mon Sep 17 00:00:00 2001 From: Yeliz Henden Date: Tue, 14 Jan 2025 14:15:23 +0000 Subject: [PATCH 05/13] little fix --- .../spectral/ipa/__tests__/utils/exceptions.test.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/spectral/ipa/__tests__/utils/exceptions.test.js b/tools/spectral/ipa/__tests__/utils/exceptions.test.js index 1f140cbdf1..6646e861a6 100644 --- a/tools/spectral/ipa/__tests__/utils/exceptions.test.js +++ b/tools/spectral/ipa/__tests__/utils/exceptions.test.js @@ -40,16 +40,16 @@ const objectWithIpa100And101Exception = { describe('tools/spectral/ipa/rulesets/functions/utils/exceptions.js', () => { describe('hasException', () => { it('returns true if object has exception matching the rule name', () => { - expect(hasException(objectWithIpa100Exception, TEST_RULE_NAME_100, '')).toBe(true); - expect(hasException(objectWithIpa100ExceptionAndOwnerExtension, TEST_RULE_NAME_100, '')).toBe(true); - expect(hasException(objectWithIpa100And101Exception, TEST_RULE_NAME_100, '')).toBe(true); + expect(hasException(objectWithIpa100Exception, TEST_RULE_NAME_100)).toBe(true); + expect(hasException(objectWithIpa100ExceptionAndOwnerExtension, TEST_RULE_NAME_100)).toBe(true); + expect(hasException(objectWithIpa100And101Exception, TEST_RULE_NAME_100)).toBe(true); }); it('returns false if object does not have exception matching the rule name', () => { - expect(hasException({}, TEST_RULE_NAME_100, '')).toBe(false); - expect(hasException(objectWithIpa101Exception, TEST_RULE_NAME_100, '')).toBe(false); + expect(hasException({}, TEST_RULE_NAME_100)).toBe(false); + expect(hasException(objectWithIpa101Exception, TEST_RULE_NAME_100)).toBe(false); }); it('returns false if object has nested exception matching the rule name', () => { - expect(hasException(objectWithNestedIpa100Exception, TEST_RULE_NAME_100, '')).toBe(false); + expect(hasException(objectWithNestedIpa100Exception, TEST_RULE_NAME_100)).toBe(false); }); }); }); From 4b48f99d6f59f5e0c84bd9223642d58d9967b58f Mon Sep 17 00:00:00 2001 From: Yeliz Henden Date: Tue, 14 Jan 2025 14:31:35 +0000 Subject: [PATCH 06/13] fix: collect violations and adoptions in helper functions --- .../eachCustomMethodMustBeGetOrPost.js | 14 +++++----- .../eachCustomMethodMustUseCamelCase.js | 20 ++++++++------ .../eachEnumValueMustBeUpperSnakeCase.js | 10 ++++--- ...ternatesBetweenResourceNameAndPathParam.js | 11 ++++---- .../functions/eachResourceHasGetMethod.js | 26 +++++++------------ .../functions/exceptionExtensionFormat.js | 9 ++++--- .../rulesets/functions/singletonHasNoId.js | 14 +++++----- .../functions/utils/collectionUtils.js | 17 ++++++++++++ 8 files changed, 70 insertions(+), 51 deletions(-) create mode 100644 tools/spectral/ipa/rulesets/functions/utils/collectionUtils.js diff --git a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js index c7d638b8a3..015bb326f0 100644 --- a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js +++ b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js @@ -1,10 +1,12 @@ import { isCustomMethod } from './utils/resourceEvaluation.js'; import { collectException, hasException } from './utils/exceptions.js'; -import collector, { EntryType } from '../../metrics/collector.js'; +import { + collectAdoption, + collectAndReturnViolation, +} from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-109-custom-method-must-be-GET-or-POST'; const ERROR_MESSAGE = 'The HTTP method for custom methods must be GET or POST.'; -const ERROR_RESULT = [{ message: ERROR_MESSAGE }]; const VALID_METHODS = ['get', 'post']; const HTTP_METHODS = ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace']; @@ -25,17 +27,15 @@ export default (input, opts, { path }) => { // Check for invalid methods if (httpMethods.some((method) => !VALID_METHODS.includes(method))) { - collector.add(EntryType.VIOLATION, path, RULE_NAME); - return ERROR_RESULT; + return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE); } // Check for multiple valid methods const validMethodCount = httpMethods.filter((method) => VALID_METHODS.includes(method)).length; if (validMethodCount > 1) { - collector.add(EntryType.VIOLATION, path, RULE_NAME); - return ERROR_RESULT; + return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE); } - collector.add(EntryType.ADOPTION, path, RULE_NAME); + collectAdoption(path, RULE_NAME); }; diff --git a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js index 80656bb19e..7db6b310c8 100644 --- a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js +++ b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js @@ -1,7 +1,10 @@ import { getCustomMethodName, isCustomMethod } from './utils/resourceEvaluation.js'; -import { hasException } from './utils/exceptions.js'; +import { collectException, hasException } from './utils/exceptions.js'; import { casing } from '@stoplight/spectral-functions'; -import collector, { EntryType } from '../../metrics/collector.js'; +import { + collectAdoption, + collectAndReturnViolation, +} from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-109-custom-method-must-use-camel-case'; @@ -11,20 +14,21 @@ export default (input, opts, { path }) => { if (!isCustomMethod(pathKey)) return; - if (hasException(input, RULE_NAME, path)) { + if (hasException(input, RULE_NAME)) { + collectException(input, RULE_NAME, path); return; } let methodName = getCustomMethodName(pathKey); if (methodName.length === 0 || methodName.trim().length === 0) { - collector.add(EntryType.VIOLATION, path, RULE_NAME); - return [{ message: 'Custom method name cannot be empty or blank.' }]; + const errorMessage = 'Custom method name cannot be empty or blank.'; + return collectAndReturnViolation(path, RULE_NAME, errorMessage); } if (casing(methodName, { type: 'camel', disallowDigits: true })) { - collector.add(EntryType.VIOLATION, path, RULE_NAME); - return [{ message: `${methodName} must use camelCase format.` }]; + const errorMessage = `${methodName} must use camelCase format.`; + return collectAndReturnViolation(path, RULE_NAME, errorMessage); } - collector.add(EntryType.ADOPTION, path, RULE_NAME); + collectAdoption(path, RULE_NAME); }; diff --git a/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js b/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js index 980e39641f..bee0ddd125 100644 --- a/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js +++ b/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js @@ -1,7 +1,10 @@ import { collectException, hasException } from './utils/exceptions.js'; import { resolveObject } from './utils/componentUtils.js'; import { casing } from '@stoplight/spectral-functions'; -import collector, { EntryType } from '../../metrics/collector.js'; +import { + collectAdoption, + collectAndReturnViolation, +} from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-123-enum-values-must-be-upper-snake-case'; const ERROR_MESSAGE = 'enum value must be UPPER_SNAKE_CASE.'; @@ -36,10 +39,9 @@ export default (input, _, { path, documentInventory }) => { }); if (errors.length === 0) { - collector.add(EntryType.ADOPTION, schemaPath, RULE_NAME); + collectAdoption(schemaPath, RULE_NAME); } else { - collector.add(EntryType.VIOLATION, schemaPath, RULE_NAME); + return collectAndReturnViolation(path, RULE_NAME, errors); } - return errors; }; diff --git a/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js b/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js index 7e1b550deb..4137874a7d 100644 --- a/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js +++ b/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js @@ -1,10 +1,12 @@ import { isPathParam } from './utils/componentUtils.js'; import { collectException, hasException } from './utils/exceptions.js'; -import collector, { EntryType } from '../../metrics/collector.js'; +import { + collectAdoption, + collectAndReturnViolation, +} from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-102-path-alternate-resource-name-path-param'; const ERROR_MESSAGE = 'API paths must alternate between resource name and path params.'; -const ERROR_RESULT = [{ message: ERROR_MESSAGE }]; const AUTH_PREFIX = '/api/atlas/v2'; const UNAUTH_PREFIX = '/api/atlas/v2/unauth'; @@ -45,9 +47,8 @@ export default (input, _, { path, documentInventory }) => { let suffix = suffixWithLeadingSlash.slice(1); let elements = suffix.split('/'); if (!validatePathStructure(elements)) { - collector.add(EntryType.VIOLATION, path, RULE_NAME); - return ERROR_RESULT; + return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE); } - collector.add(EntryType.ADOPTION, path, RULE_NAME); + collectAdoption(path, RULE_NAME); }; diff --git a/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js b/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js index 95138ed55f..b6a1aad84f 100644 --- a/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js +++ b/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js @@ -6,8 +6,11 @@ import { isSingletonResource, getResourcePaths, } from './utils/resourceEvaluation.js'; -import { hasException } from './utils/exceptions.js'; -import collector, { EntryType } from '../../metrics/collector.js'; +import { collectException, hasException } from './utils/exceptions.js'; +import { + collectAdoption, + collectAndReturnViolation, +} from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-104-resource-has-GET'; const ERROR_MESSAGE = 'APIs must provide a get method for resources.'; @@ -19,7 +22,8 @@ export default (input, _, { path, documentInventory }) => { const oas = documentInventory.resolved; - if (hasException(oas.paths[input], RULE_NAME, path)) { + if (hasException(oas.paths[input], RULE_NAME)) { + collectException(oas.paths[input], RULE_NAME, path); return; } @@ -27,23 +31,13 @@ export default (input, _, { path, documentInventory }) => { if (isSingletonResource(resourcePaths)) { if (!hasGetMethod(oas.paths[resourcePaths[0]])) { - collector.add(EntryType.VIOLATION, path, RULE_NAME); - return [ - { - message: ERROR_MESSAGE, - }, - ]; + return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE); } } else if (isStandardResource(resourcePaths)) { if (!hasGetMethod(oas.paths[resourcePaths[1]])) { - collector.add(EntryType.VIOLATION, path, RULE_NAME); - return [ - { - message: ERROR_MESSAGE, - }, - ]; + return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE); } } - collector.add(EntryType.ADOPTION, path, RULE_NAME); + collectAdoption(path, RULE_NAME); }; diff --git a/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js b/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js index 46ba282803..41dd90b7ce 100644 --- a/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js +++ b/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js @@ -1,4 +1,7 @@ -import collector, { EntryType } from '../../metrics/collector.js'; +import { + collectAdoption, + collectAndReturnViolation, +} from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-005-exception-extension-format'; const ERROR_MESSAGE = 'IPA exceptions must have a valid rule name and a reason.'; @@ -20,9 +23,9 @@ export default (input, _, { path }) => { }); if (errors.length === 0) { - collector.add(EntryType.ADOPTION, path, RULE_NAME); + collectAdoption(path, RULE_NAME); } else { - collector.add(EntryType.VIOLATION, path, RULE_NAME); + return collectAndReturnViolation(path, RULE_NAME, errors); } return errors; diff --git a/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js b/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js index 9fcccd3ea2..fd9bdfb59f 100644 --- a/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js +++ b/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js @@ -7,7 +7,10 @@ import { } from './utils/resourceEvaluation.js'; import { collectException, hasException } from './utils/exceptions.js'; import { getAllSuccessfulGetResponseSchemas } from './utils/methodUtils.js'; -import collector, { EntryType } from '../../metrics/collector.js'; +import { + collectAdoption, + collectAndReturnViolation, +} from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-113-singleton-must-not-have-id'; const ERROR_MESSAGE = 'Singleton resources must not have a user-provided or system-generated ID.'; @@ -30,16 +33,11 @@ export default (input, opts, { path, documentInventory }) => { if (isSingletonResource(resourcePaths) && hasGetMethod(input)) { const resourceSchemas = getAllSuccessfulGetResponseSchemas(input); if (resourceSchemas.some((schema) => schemaHasIdProperty(schema))) { - collector.add(EntryType.VIOLATION, path, RULE_NAME); - return [ - { - message: ERROR_MESSAGE, - }, - ]; + return collectAndReturnViolation(path, RULE_NAME, ERROR_MESSAGE); } } - collector.add(EntryType.ADOPTION, path, RULE_NAME); + collectAdoption(path, RULE_NAME); }; function schemaHasIdProperty(schema) { diff --git a/tools/spectral/ipa/rulesets/functions/utils/collectionUtils.js b/tools/spectral/ipa/rulesets/functions/utils/collectionUtils.js new file mode 100644 index 0000000000..cce43df62b --- /dev/null +++ b/tools/spectral/ipa/rulesets/functions/utils/collectionUtils.js @@ -0,0 +1,17 @@ +import collector, { EntryType } from '../../../metrics/collector.js'; + +export function collectAndReturnViolation(path, ruleName, errorData) { + collector.add(EntryType.VIOLATION, path, ruleName); + + if (typeof errorData === 'string') { + return [{ message: errorData }]; + } else if (Array.isArray(errorData)) { + return errorData; + } else { + throw new Error('Invalid error data type. Expected string or array.'); + } +} + +export function collectAdoption(path, ruleName) { + collector.add(EntryType.ADOPTION, path, ruleName); +} \ No newline at end of file From 695af05a3b49c5a7afcff4bd4b7d75783ef2d1c5 Mon Sep 17 00:00:00 2001 From: Yeliz Henden Date: Tue, 14 Jan 2025 14:43:34 +0000 Subject: [PATCH 07/13] fix: add jsdoc --- tools/spectral/ipa/metrics/collector.js | 6 +++--- .../eachCustomMethodMustBeGetOrPost.js | 5 +---- .../eachCustomMethodMustUseCamelCase.js | 5 +---- .../eachEnumValueMustBeUpperSnakeCase.js | 6 +----- ...lternatesBetweenResourceNameAndPathParam.js | 5 +---- .../functions/eachResourceHasGetMethod.js | 5 +---- .../functions/exceptionExtensionFormat.js | 5 +---- .../ipa/rulesets/functions/singletonHasNoId.js | 7 ++----- .../functions/utils/collectionUtils.js | 18 +++++++++++++++++- .../ipa/rulesets/functions/utils/exceptions.js | 8 ++++---- 10 files changed, 32 insertions(+), 38 deletions(-) diff --git a/tools/spectral/ipa/metrics/collector.js b/tools/spectral/ipa/metrics/collector.js index cc92b96d40..15ec5131d2 100644 --- a/tools/spectral/ipa/metrics/collector.js +++ b/tools/spectral/ipa/metrics/collector.js @@ -36,16 +36,16 @@ class Collector { }); } - add(type, componentId, ruleName, exceptionReason = null ) { + add(type, componentId, ruleName, exceptionReason = null) { if (componentId && ruleName && type) { if (!Object.values(EntryType).includes(type)) { throw new Error(`Invalid entry type: ${type}`); } componentId = componentId.join('.'); - const entry = {componentId, ruleName}; + const entry = { componentId, ruleName }; - if(type === EntryType.EXCEPTION && exceptionReason) { + if (type === EntryType.EXCEPTION && exceptionReason) { entry.exceptionReason = exceptionReason; } diff --git a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js index 015bb326f0..858f290737 100644 --- a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js +++ b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js @@ -1,9 +1,6 @@ import { isCustomMethod } from './utils/resourceEvaluation.js'; import { collectException, hasException } from './utils/exceptions.js'; -import { - collectAdoption, - collectAndReturnViolation, -} from './utils/collectionUtils.js'; +import { collectAdoption, collectAndReturnViolation } from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-109-custom-method-must-be-GET-or-POST'; const ERROR_MESSAGE = 'The HTTP method for custom methods must be GET or POST.'; diff --git a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js index 7db6b310c8..3879c6df82 100644 --- a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js +++ b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js @@ -1,10 +1,7 @@ import { getCustomMethodName, isCustomMethod } from './utils/resourceEvaluation.js'; import { collectException, hasException } from './utils/exceptions.js'; import { casing } from '@stoplight/spectral-functions'; -import { - collectAdoption, - collectAndReturnViolation, -} from './utils/collectionUtils.js'; +import { collectAdoption, collectAndReturnViolation } from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-109-custom-method-must-use-camel-case'; diff --git a/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js b/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js index bee0ddd125..3e07338d8b 100644 --- a/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js +++ b/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js @@ -1,10 +1,7 @@ import { collectException, hasException } from './utils/exceptions.js'; import { resolveObject } from './utils/componentUtils.js'; import { casing } from '@stoplight/spectral-functions'; -import { - collectAdoption, - collectAndReturnViolation, -} from './utils/collectionUtils.js'; +import { collectAdoption, collectAndReturnViolation } from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-123-enum-values-must-be-upper-snake-case'; const ERROR_MESSAGE = 'enum value must be UPPER_SNAKE_CASE.'; @@ -43,5 +40,4 @@ export default (input, _, { path, documentInventory }) => { } else { return collectAndReturnViolation(path, RULE_NAME, errors); } - }; diff --git a/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js b/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js index 4137874a7d..abc44dc3f7 100644 --- a/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js +++ b/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js @@ -1,9 +1,6 @@ import { isPathParam } from './utils/componentUtils.js'; import { collectException, hasException } from './utils/exceptions.js'; -import { - collectAdoption, - collectAndReturnViolation, -} from './utils/collectionUtils.js'; +import { collectAdoption, collectAndReturnViolation } from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-102-path-alternate-resource-name-path-param'; const ERROR_MESSAGE = 'API paths must alternate between resource name and path params.'; diff --git a/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js b/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js index b6a1aad84f..99e9548ae5 100644 --- a/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js +++ b/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js @@ -7,10 +7,7 @@ import { getResourcePaths, } from './utils/resourceEvaluation.js'; import { collectException, hasException } from './utils/exceptions.js'; -import { - collectAdoption, - collectAndReturnViolation, -} from './utils/collectionUtils.js'; +import { collectAdoption, collectAndReturnViolation } from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-104-resource-has-GET'; const ERROR_MESSAGE = 'APIs must provide a get method for resources.'; diff --git a/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js b/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js index 41dd90b7ce..21289d08c6 100644 --- a/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js +++ b/tools/spectral/ipa/rulesets/functions/exceptionExtensionFormat.js @@ -1,7 +1,4 @@ -import { - collectAdoption, - collectAndReturnViolation, -} from './utils/collectionUtils.js'; +import { collectAdoption, collectAndReturnViolation } from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-005-exception-extension-format'; const ERROR_MESSAGE = 'IPA exceptions must have a valid rule name and a reason.'; diff --git a/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js b/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js index fd9bdfb59f..8cce695180 100644 --- a/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js +++ b/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js @@ -7,10 +7,7 @@ import { } from './utils/resourceEvaluation.js'; import { collectException, hasException } from './utils/exceptions.js'; import { getAllSuccessfulGetResponseSchemas } from './utils/methodUtils.js'; -import { - collectAdoption, - collectAndReturnViolation, -} from './utils/collectionUtils.js'; +import { collectAdoption, collectAndReturnViolation } from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-113-singleton-must-not-have-id'; const ERROR_MESSAGE = 'Singleton resources must not have a user-provided or system-generated ID.'; @@ -23,7 +20,7 @@ export default (input, opts, { path, documentInventory }) => { } if (hasException(input, RULE_NAME)) { - collectException(input, RULE_NAME, path) + collectException(input, RULE_NAME, path); return; } diff --git a/tools/spectral/ipa/rulesets/functions/utils/collectionUtils.js b/tools/spectral/ipa/rulesets/functions/utils/collectionUtils.js index cce43df62b..3c5febfd64 100644 --- a/tools/spectral/ipa/rulesets/functions/utils/collectionUtils.js +++ b/tools/spectral/ipa/rulesets/functions/utils/collectionUtils.js @@ -1,5 +1,15 @@ import collector, { EntryType } from '../../../metrics/collector.js'; +/** + * Collects a violation entry and returns formatted error data. + * + * @param {string} path - The JSON path for the object where the rule violation occurred. + * @param {string} ruleName - The name of the rule that was violated. + * @param {string|Array} errorData - The error information. Can be either a string message or an array of error objects. + * @returns {Array} An array of error objects. Each object has a 'message' property. + * @throws {Error} Throws an error if errorData is neither a string nor an array. + * + */ export function collectAndReturnViolation(path, ruleName, errorData) { collector.add(EntryType.VIOLATION, path, ruleName); @@ -12,6 +22,12 @@ export function collectAndReturnViolation(path, ruleName, errorData) { } } +/** + * Collects an adoption entry. + * + * @param {string} path - The JSON path for the object where the rule adoption occurred. + * @param {string} ruleName - The name of the rule that was adopted. + */ export function collectAdoption(path, ruleName) { collector.add(EntryType.ADOPTION, path, ruleName); -} \ No newline at end of file +} diff --git a/tools/spectral/ipa/rulesets/functions/utils/exceptions.js b/tools/spectral/ipa/rulesets/functions/utils/exceptions.js index 244c11e08a..f0ce399129 100644 --- a/tools/spectral/ipa/rulesets/functions/utils/exceptions.js +++ b/tools/spectral/ipa/rulesets/functions/utils/exceptions.js @@ -17,15 +17,15 @@ export function hasException(object, ruleName) { } /** - * Collects the information about the object if the object has an exception defined for the given rule + * Collects an exception entry. * * @param object the object to evaluate - * @param ruleName the name of the exempted rule - * @param path the JSON path to the object + * @param {string} path - The JSON path for the object where the rule exception occurred. + * @param {string} ruleName - The name of the rule that the exception is defined for. */ export function collectException(object, ruleName, path) { let exceptionReason = object[EXCEPTION_EXTENSION][ruleName]; - if(exceptionReason) { + if (exceptionReason) { collector.add(EntryType.EXCEPTION, path, ruleName, exceptionReason); } } From 3ab740a019d2198d0bfd0f4ef6d5926a50e5064b Mon Sep 17 00:00:00 2001 From: Yeliz Henden Date: Tue, 14 Jan 2025 14:46:12 +0000 Subject: [PATCH 08/13] fix: all collect helpers in collectionUtils file --- .../functions/eachCustomMethodMustBeGetOrPost.js | 8 ++++++-- .../eachCustomMethodMustUseCamelCase.js | 8 ++++++-- .../eachEnumValueMustBeUpperSnakeCase.js | 8 ++++++-- ...hAlternatesBetweenResourceNameAndPathParam.js | 8 ++++++-- .../functions/eachResourceHasGetMethod.js | 8 ++++++-- .../ipa/rulesets/functions/singletonHasNoId.js | 8 ++++++-- .../rulesets/functions/utils/collectionUtils.js | 15 +++++++++++++++ .../ipa/rulesets/functions/utils/exceptions.js | 16 ---------------- 8 files changed, 51 insertions(+), 28 deletions(-) diff --git a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js index 858f290737..596da83c69 100644 --- a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js +++ b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js @@ -1,6 +1,10 @@ import { isCustomMethod } from './utils/resourceEvaluation.js'; -import { collectException, hasException } from './utils/exceptions.js'; -import { collectAdoption, collectAndReturnViolation } from './utils/collectionUtils.js'; +import { hasException } from './utils/exceptions.js'; +import { + collectAdoption, + collectAndReturnViolation, + collectException, +} from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-109-custom-method-must-be-GET-or-POST'; const ERROR_MESSAGE = 'The HTTP method for custom methods must be GET or POST.'; diff --git a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js index 3879c6df82..6a215d6abd 100644 --- a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js +++ b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js @@ -1,7 +1,11 @@ import { getCustomMethodName, isCustomMethod } from './utils/resourceEvaluation.js'; -import { collectException, hasException } from './utils/exceptions.js'; +import { hasException } from './utils/exceptions.js'; import { casing } from '@stoplight/spectral-functions'; -import { collectAdoption, collectAndReturnViolation } from './utils/collectionUtils.js'; +import { + collectAdoption, + collectAndReturnViolation, + collectException, +} from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-109-custom-method-must-use-camel-case'; diff --git a/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js b/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js index 3e07338d8b..2507e9f193 100644 --- a/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js +++ b/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js @@ -1,7 +1,11 @@ -import { collectException, hasException } from './utils/exceptions.js'; +import { hasException } from './utils/exceptions.js'; import { resolveObject } from './utils/componentUtils.js'; import { casing } from '@stoplight/spectral-functions'; -import { collectAdoption, collectAndReturnViolation } from './utils/collectionUtils.js'; +import { + collectAdoption, + collectAndReturnViolation, + collectException, +} from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-123-enum-values-must-be-upper-snake-case'; const ERROR_MESSAGE = 'enum value must be UPPER_SNAKE_CASE.'; diff --git a/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js b/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js index abc44dc3f7..47ecedba8f 100644 --- a/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js +++ b/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js @@ -1,6 +1,10 @@ import { isPathParam } from './utils/componentUtils.js'; -import { collectException, hasException } from './utils/exceptions.js'; -import { collectAdoption, collectAndReturnViolation } from './utils/collectionUtils.js'; +import { hasException } from './utils/exceptions.js'; +import { + collectAdoption, + collectAndReturnViolation, + collectException, +} from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-102-path-alternate-resource-name-path-param'; const ERROR_MESSAGE = 'API paths must alternate between resource name and path params.'; diff --git a/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js b/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js index 99e9548ae5..79fe80a599 100644 --- a/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js +++ b/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js @@ -6,8 +6,12 @@ import { isSingletonResource, getResourcePaths, } from './utils/resourceEvaluation.js'; -import { collectException, hasException } from './utils/exceptions.js'; -import { collectAdoption, collectAndReturnViolation } from './utils/collectionUtils.js'; +import { hasException } from './utils/exceptions.js'; +import { + collectAdoption, + collectAndReturnViolation, + collectException, +} from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-104-resource-has-GET'; const ERROR_MESSAGE = 'APIs must provide a get method for resources.'; diff --git a/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js b/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js index 8cce695180..fcc69309b6 100644 --- a/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js +++ b/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js @@ -5,9 +5,13 @@ import { isCustomMethod, isSingletonResource, } from './utils/resourceEvaluation.js'; -import { collectException, hasException } from './utils/exceptions.js'; +import { hasException } from './utils/exceptions.js'; import { getAllSuccessfulGetResponseSchemas } from './utils/methodUtils.js'; -import { collectAdoption, collectAndReturnViolation } from './utils/collectionUtils.js'; +import { + collectAdoption, + collectAndReturnViolation, + collectException, +} from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-113-singleton-must-not-have-id'; const ERROR_MESSAGE = 'Singleton resources must not have a user-provided or system-generated ID.'; diff --git a/tools/spectral/ipa/rulesets/functions/utils/collectionUtils.js b/tools/spectral/ipa/rulesets/functions/utils/collectionUtils.js index 3c5febfd64..d915e42d15 100644 --- a/tools/spectral/ipa/rulesets/functions/utils/collectionUtils.js +++ b/tools/spectral/ipa/rulesets/functions/utils/collectionUtils.js @@ -31,3 +31,18 @@ export function collectAndReturnViolation(path, ruleName, errorData) { export function collectAdoption(path, ruleName) { collector.add(EntryType.ADOPTION, path, ruleName); } + +/** + * Collects an exception entry. + * + * @param object the object to evaluate + * @param {string} path - The JSON path for the object where the rule exception occurred. + * @param {string} ruleName - The name of the rule that the exception is defined for. + */ +export function collectException(object, ruleName, path) { + const EXCEPTION_EXTENSION = 'x-xgen-IPA-exception'; + let exceptionReason = object[EXCEPTION_EXTENSION][ruleName]; + if (exceptionReason) { + collector.add(EntryType.EXCEPTION, path, ruleName, exceptionReason); + } +} diff --git a/tools/spectral/ipa/rulesets/functions/utils/exceptions.js b/tools/spectral/ipa/rulesets/functions/utils/exceptions.js index f0ce399129..91b6f84f68 100644 --- a/tools/spectral/ipa/rulesets/functions/utils/exceptions.js +++ b/tools/spectral/ipa/rulesets/functions/utils/exceptions.js @@ -1,5 +1,3 @@ -import collector, { EntryType } from '../../../metrics/collector.js'; - const EXCEPTION_EXTENSION = 'x-xgen-IPA-exception'; /** @@ -15,17 +13,3 @@ export function hasException(object, ruleName) { } return false; } - -/** - * Collects an exception entry. - * - * @param object the object to evaluate - * @param {string} path - The JSON path for the object where the rule exception occurred. - * @param {string} ruleName - The name of the rule that the exception is defined for. - */ -export function collectException(object, ruleName, path) { - let exceptionReason = object[EXCEPTION_EXTENSION][ruleName]; - if (exceptionReason) { - collector.add(EntryType.EXCEPTION, path, ruleName, exceptionReason); - } -} From d83044c404e9cea0ba3f825425c72a869d9edd3e Mon Sep 17 00:00:00 2001 From: Yeliz Henden Date: Tue, 14 Jan 2025 14:49:05 +0000 Subject: [PATCH 09/13] little fix: schemaPath --- .../rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js b/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js index 2507e9f193..3cd92cc6c1 100644 --- a/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js +++ b/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js @@ -23,7 +23,7 @@ export default (input, _, { path, documentInventory }) => { const schemaPath = getSchemaPathFromEnumPath(path); const schemaObject = resolveObject(oas, schemaPath); if (hasException(schemaObject, RULE_NAME)) { - collectException(schemaObject, RULE_NAME, path); + collectException(schemaObject, RULE_NAME, schemaPath); return; } @@ -42,6 +42,6 @@ export default (input, _, { path, documentInventory }) => { if (errors.length === 0) { collectAdoption(schemaPath, RULE_NAME); } else { - return collectAndReturnViolation(path, RULE_NAME, errors); + return collectAndReturnViolation(schemaPath, RULE_NAME, errors); } }; From f1a52c058e03747fce371d725d83386617c68eff Mon Sep 17 00:00:00 2001 From: Yeliz Henden Date: Tue, 14 Jan 2025 14:49:19 +0000 Subject: [PATCH 10/13] prettier fix --- .../rulesets/functions/eachCustomMethodMustBeGetOrPost.js | 6 +----- .../rulesets/functions/eachCustomMethodMustUseCamelCase.js | 6 +----- .../rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js | 6 +----- .../eachPathAlternatesBetweenResourceNameAndPathParam.js | 6 +----- .../ipa/rulesets/functions/eachResourceHasGetMethod.js | 6 +----- tools/spectral/ipa/rulesets/functions/singletonHasNoId.js | 6 +----- 6 files changed, 6 insertions(+), 30 deletions(-) diff --git a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js index 596da83c69..3327c2af31 100644 --- a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js +++ b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustBeGetOrPost.js @@ -1,10 +1,6 @@ import { isCustomMethod } from './utils/resourceEvaluation.js'; import { hasException } from './utils/exceptions.js'; -import { - collectAdoption, - collectAndReturnViolation, - collectException, -} from './utils/collectionUtils.js'; +import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-109-custom-method-must-be-GET-or-POST'; const ERROR_MESSAGE = 'The HTTP method for custom methods must be GET or POST.'; diff --git a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js index 6a215d6abd..2862054155 100644 --- a/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js +++ b/tools/spectral/ipa/rulesets/functions/eachCustomMethodMustUseCamelCase.js @@ -1,11 +1,7 @@ import { getCustomMethodName, isCustomMethod } from './utils/resourceEvaluation.js'; import { hasException } from './utils/exceptions.js'; import { casing } from '@stoplight/spectral-functions'; -import { - collectAdoption, - collectAndReturnViolation, - collectException, -} from './utils/collectionUtils.js'; +import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-109-custom-method-must-use-camel-case'; diff --git a/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js b/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js index 3cd92cc6c1..7f798e361e 100644 --- a/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js +++ b/tools/spectral/ipa/rulesets/functions/eachEnumValueMustBeUpperSnakeCase.js @@ -1,11 +1,7 @@ import { hasException } from './utils/exceptions.js'; import { resolveObject } from './utils/componentUtils.js'; import { casing } from '@stoplight/spectral-functions'; -import { - collectAdoption, - collectAndReturnViolation, - collectException, -} from './utils/collectionUtils.js'; +import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-123-enum-values-must-be-upper-snake-case'; const ERROR_MESSAGE = 'enum value must be UPPER_SNAKE_CASE.'; diff --git a/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js b/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js index 47ecedba8f..d432f9686f 100644 --- a/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js +++ b/tools/spectral/ipa/rulesets/functions/eachPathAlternatesBetweenResourceNameAndPathParam.js @@ -1,10 +1,6 @@ import { isPathParam } from './utils/componentUtils.js'; import { hasException } from './utils/exceptions.js'; -import { - collectAdoption, - collectAndReturnViolation, - collectException, -} from './utils/collectionUtils.js'; +import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-102-path-alternate-resource-name-path-param'; const ERROR_MESSAGE = 'API paths must alternate between resource name and path params.'; diff --git a/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js b/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js index 79fe80a599..34388b7bf6 100644 --- a/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js +++ b/tools/spectral/ipa/rulesets/functions/eachResourceHasGetMethod.js @@ -7,11 +7,7 @@ import { getResourcePaths, } from './utils/resourceEvaluation.js'; import { hasException } from './utils/exceptions.js'; -import { - collectAdoption, - collectAndReturnViolation, - collectException, -} from './utils/collectionUtils.js'; +import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-104-resource-has-GET'; const ERROR_MESSAGE = 'APIs must provide a get method for resources.'; diff --git a/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js b/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js index fcc69309b6..d208774fbd 100644 --- a/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js +++ b/tools/spectral/ipa/rulesets/functions/singletonHasNoId.js @@ -7,11 +7,7 @@ import { } from './utils/resourceEvaluation.js'; import { hasException } from './utils/exceptions.js'; import { getAllSuccessfulGetResponseSchemas } from './utils/methodUtils.js'; -import { - collectAdoption, - collectAndReturnViolation, - collectException, -} from './utils/collectionUtils.js'; +import { collectAdoption, collectAndReturnViolation, collectException } from './utils/collectionUtils.js'; const RULE_NAME = 'xgen-IPA-113-singleton-must-not-have-id'; const ERROR_MESSAGE = 'Singleton resources must not have a user-provided or system-generated ID.'; From 8c4b9a68826486b562fffbbdb5eb5f60ab40e636 Mon Sep 17 00:00:00 2001 From: Yeliz Henden Date: Tue, 14 Jan 2025 14:53:28 +0000 Subject: [PATCH 11/13] fix: import exception_extension --- tools/spectral/ipa/rulesets/functions/utils/collectionUtils.js | 3 +-- tools/spectral/ipa/rulesets/functions/utils/exceptions.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/spectral/ipa/rulesets/functions/utils/collectionUtils.js b/tools/spectral/ipa/rulesets/functions/utils/collectionUtils.js index d915e42d15..9cfd621d16 100644 --- a/tools/spectral/ipa/rulesets/functions/utils/collectionUtils.js +++ b/tools/spectral/ipa/rulesets/functions/utils/collectionUtils.js @@ -1,5 +1,5 @@ import collector, { EntryType } from '../../../metrics/collector.js'; - +import { EXCEPTION_EXTENSION } from './exceptions.js'; /** * Collects a violation entry and returns formatted error data. * @@ -40,7 +40,6 @@ export function collectAdoption(path, ruleName) { * @param {string} ruleName - The name of the rule that the exception is defined for. */ export function collectException(object, ruleName, path) { - const EXCEPTION_EXTENSION = 'x-xgen-IPA-exception'; let exceptionReason = object[EXCEPTION_EXTENSION][ruleName]; if (exceptionReason) { collector.add(EntryType.EXCEPTION, path, ruleName, exceptionReason); diff --git a/tools/spectral/ipa/rulesets/functions/utils/exceptions.js b/tools/spectral/ipa/rulesets/functions/utils/exceptions.js index 91b6f84f68..256cab89dc 100644 --- a/tools/spectral/ipa/rulesets/functions/utils/exceptions.js +++ b/tools/spectral/ipa/rulesets/functions/utils/exceptions.js @@ -1,4 +1,4 @@ -const EXCEPTION_EXTENSION = 'x-xgen-IPA-exception'; +export const EXCEPTION_EXTENSION = 'x-xgen-IPA-exception'; /** * Checks if the object has an exception extension "x-xgen-IPA-exception" From c00e9d7fc7b79b718594e628cade10fb86911cf3 Mon Sep 17 00:00:00 2001 From: Yeliz Henden Date: Tue, 14 Jan 2025 14:54:00 +0000 Subject: [PATCH 12/13] fix: update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7097f61c71..66953c4020 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ *.vscode *.out +/tools/spectral/ipa/ipa-collector-results-combined.log From 56e520f420cadf76533e34998297972c86aed894 Mon Sep 17 00:00:00 2001 From: Yeliz Henden Date: Tue, 14 Jan 2025 15:14:01 +0000 Subject: [PATCH 13/13] fix: update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 66953c4020..87dae3c541 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,4 @@ *.vscode *.out -/tools/spectral/ipa/ipa-collector-results-combined.log +**/*ipa-collector-results-combined.log