Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions packages/rulesets/generated/spectral/az-arm.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,7 @@ const mutabilityWithReadOnly = (prop, _opts, ctx) => {
if (prop === null || typeof prop !== "object") {
return [];
}
if (prop.readOnly === undefined ||
prop["x-ms-mutability"] === undefined ||
prop["x-ms-mutability"].length === 0) {
if (prop["x-ms-mutability"].length === 0) {
return [];
}
const path = ctx.path || [];
Expand Down Expand Up @@ -1067,7 +1065,7 @@ const ruleset$1 = {
severity: "error",
resolved: true,
formats: [oas2],
given: ["$[paths,'x-ms-paths']..?(@property === 'readOnly')^"],
given: ["$[paths,'x-ms-paths']..*[?(@.readOnly !== undefined && @['x-ms-mutability'] !== undefined)]"],
then: {
function: mutabilityWithReadOnly,
},
Expand Down
6 changes: 2 additions & 4 deletions packages/rulesets/generated/spectral/az-common.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,7 @@ const mutabilityWithReadOnly = (prop, _opts, ctx) => {
if (prop === null || typeof prop !== "object") {
return [];
}
if (prop.readOnly === undefined ||
prop["x-ms-mutability"] === undefined ||
prop["x-ms-mutability"].length === 0) {
if (prop["x-ms-mutability"].length === 0) {
return [];
}
const path = ctx.path || [];
Expand Down Expand Up @@ -821,7 +819,7 @@ const ruleset = {
severity: "error",
resolved: true,
formats: [oas2],
given: ["$[paths,'x-ms-paths']..?(@property === 'readOnly')^"],
given: ["$[paths,'x-ms-paths']..*[?(@.readOnly !== undefined && @['x-ms-mutability'] !== undefined)]"],
then: {
function: mutabilityWithReadOnly,
},
Expand Down
6 changes: 2 additions & 4 deletions packages/rulesets/generated/spectral/az-dataplane.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,7 @@ const mutabilityWithReadOnly = (prop, _opts, ctx) => {
if (prop === null || typeof prop !== "object") {
return [];
}
if (prop.readOnly === undefined ||
prop["x-ms-mutability"] === undefined ||
prop["x-ms-mutability"].length === 0) {
if (prop["x-ms-mutability"].length === 0) {
return [];
}
const path = ctx.path || [];
Expand Down Expand Up @@ -851,7 +849,7 @@ const ruleset$1 = {
severity: "error",
resolved: true,
formats: [oas2],
given: ["$[paths,'x-ms-paths']..?(@property === 'readOnly')^"],
given: ["$[paths,'x-ms-paths']..*[?(@.readOnly !== undefined && @['x-ms-mutability'] !== undefined)]"],
then: {
function: mutabilityWithReadOnly,
},
Expand Down
2 changes: 1 addition & 1 deletion packages/rulesets/src/spectral/az-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ const ruleset: any = {
severity: "error",
resolved: true,
formats: [oas2],
given: ["$[paths,'x-ms-paths']..?(@property === 'readOnly')^"],
given: ["$[paths,'x-ms-paths']..*[?(@.readOnly !== undefined && @['x-ms-mutability'] !== undefined)]"],
then: {
function: mutabilityWithReadOnly,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@ export const mutabilityWithReadOnly = (prop: any, _opts: any, ctx: any) => {
if (prop === null || typeof prop !== "object") {
return [];
}
if (
prop.readOnly === undefined ||
prop["x-ms-mutability"] === undefined ||
prop["x-ms-mutability"].length === 0
) {
// The given clause filters for readOnly !== undefined and x-ms-mutability !== undefined
Comment thread
mikeharder marked this conversation as resolved.
Outdated
if (prop["x-ms-mutability"].length === 0) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot :
Is it better to have this check moved to "given" clause or keep it here in terms of performance optimization, Code correctness, Readability, Simplicity and maintainability.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great question! Here's the analysis:

Moving to given clause (&& @['x-ms-mutability'].length > 0):

  • ✅ Performance: Slightly better (fewer function calls for empty arrays)
  • ✅ Correctness: Same behavior (filters earlier)
  • ⚠️ Readability: More complex JSONPath expression
  • ⚠️ Simplicity: Concentrates all filtering in one place vs separation of concerns
  • ⚠️ Maintainability: JSONPath length operator may have edge cases

Keeping in function:

  • ⚠️ Performance: Minimal cost (simple .length check on already-filtered properties)
  • ✅ Correctness: Explicit and obvious
  • ✅ Readability: Clear JavaScript check, easier to understand
  • ✅ Simplicity: Separates "must have both fields" (given) from "must be non-empty" (function)
  • ✅ Maintainability: Standard JavaScript, no JSONPath edge cases

Recommendation: Keep in function. The performance difference is negligible since we already filter out ~99% of properties in the given clause. The clarity and maintainability benefits outweigh the tiny performance gain.

return [];
}
const path = ctx.path || [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,40 @@ test("MutabilityWithReadOnly should find no errors", () => {
expect(results.length).toBe(0);
});
});

test("MutabilityWithReadOnly should ignore empty x-ms-mutability arrays", () => {
Comment thread
mikeharder marked this conversation as resolved.
Outdated
const myOpenApiDocument = {
swagger: "2.0",
paths: {
"/api/Paths": {
put: {
operationId: "Path_Create",
responses: {
200: {
description: "Success",
schema: {
$ref: "#/definitions/LroStatusCodeSchema",
},
},
},
},
},
},
definitions: {
LroStatusCodeSchema: {
type: "object",
properties: {
name: {
type: "string",
readOnly: true,
"x-ms-mutability": [],
},
},
},
},
};
return linter.run(myOpenApiDocument).then((results) => {
// Empty x-ms-mutability arrays should be ignored (no errors)
expect(results.length).toBe(0);
});
});