-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create
lro-location-header
rule to disable LroLocationHeader
Lint…
…Diff rule (#514) Closes Azure/typespec-azure-pr#3896 **REST API Specs** (9 violations) Azure/azure-rest-api-specs#28505 --------- Co-authored-by: Timothee Guerin <[email protected]>
- Loading branch information
1 parent
8d9d99a
commit f1d30de
Showing
7 changed files
with
191 additions
and
0 deletions.
There are no files selected for viewing
8 changes: 8 additions & 0 deletions
8
.chronus/changes/arm-LroLocationHeaderRule-2024-2-25-20-46-31.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking | ||
changeKind: feature | ||
packages: | ||
- "@azure-tools/typespec-azure-resource-manager" | ||
--- | ||
|
||
Add `lro-location-header` rule. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
docs/libraries/azure-resource-manager/rules/lro-location-header.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
--- | ||
title: lro-location-header | ||
--- | ||
|
||
```text title=- Full name- | ||
@azure-tools/typespec-azure-resource-manager/lro-location-header | ||
``` | ||
|
||
Long-running (LRO) operations with 202 responses must have a "Location" response header. | ||
|
||
#### ❌ Incorrect | ||
|
||
```tsp | ||
@armResourceOperations | ||
interface Employees { | ||
delete is ArmResourceDeleteWithoutOkAsync<Employee, EmployeeProperties, LroHeaders = {}>; | ||
} | ||
``` | ||
|
||
#### ✅ Correct | ||
|
||
```tsp | ||
@armResourceOperations | ||
interface Employees { | ||
delete is ArmResourceDeleteWithoutOkAsync<Employee, EmployeeProperties>; | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
packages/typespec-azure-resource-manager/src/rules/lro-location-header.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { ModelProperty, Operation, createRule } from "@typespec/compiler"; | ||
import { getHttpOperation } from "@typespec/http"; | ||
|
||
function getCaseInsensitiveHeader( | ||
headers: Record<string, ModelProperty> | undefined, | ||
key: string | ||
): string | undefined { | ||
if (!headers) { | ||
return undefined; | ||
} | ||
for (const header of Object.keys(headers)) { | ||
if (header.toLowerCase() === key.toLowerCase()) { | ||
return header; | ||
} | ||
} | ||
return undefined; | ||
} | ||
|
||
/** | ||
* Ensure that LRO 202 responses have a Location Header. | ||
*/ | ||
export const lroLocationHeaderRule = createRule({ | ||
name: "lro-location-header", | ||
severity: "warning", | ||
url: "https://azure.github.io/typespec-azure/docs/libraries/azure-resource-manager/rules/lro-location-header", | ||
description: "A 202 response should include a Location response header.", | ||
messages: { | ||
default: `A 202 response should include a Location response header.`, | ||
}, | ||
create(context) { | ||
return { | ||
operation: (op: Operation) => { | ||
const [httpOperation, _] = getHttpOperation(context.program, op); | ||
const responses = httpOperation.responses; | ||
for (const response of responses) { | ||
if (response.statusCodes !== 202) { | ||
continue; | ||
} | ||
for (const resp of response.responses) { | ||
if (getCaseInsensitiveHeader(resp.headers, "Location") === undefined) { | ||
context.reportDiagnostic({ | ||
target: op, | ||
}); | ||
return; | ||
} | ||
} | ||
} | ||
}, | ||
}; | ||
}, | ||
}); |
101 changes: 101 additions & 0 deletions
101
packages/typespec-azure-resource-manager/test/rules/lro-location-header.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
import { | ||
BasicTestRunner, | ||
LinterRuleTester, | ||
createLinterRuleTester, | ||
} from "@typespec/compiler/testing"; | ||
import { beforeEach, it } from "vitest"; | ||
import { lroLocationHeaderRule } from "../../src/rules/lro-location-header.js"; | ||
import { createAzureResourceManagerTestRunner } from "../test-host.js"; | ||
|
||
let runner: BasicTestRunner; | ||
let tester: LinterRuleTester; | ||
|
||
beforeEach(async () => { | ||
runner = await createAzureResourceManagerTestRunner(); | ||
tester = createLinterRuleTester( | ||
runner, | ||
lroLocationHeaderRule, | ||
"@azure-tools/typespec-azure-resource-manager" | ||
); | ||
}); | ||
|
||
it("Emits a warning for LRO 202 response that does not contain a Location header", async () => { | ||
await tester | ||
.expect( | ||
` | ||
@armProviderNamespace | ||
@useDependency(Azure.ResourceManager.Versions.v1_0_Preview_1) | ||
namespace Microsoft.Contoso; | ||
model Employee is ProxyResource<EmployeeProperties> { | ||
@doc("Name of employee") | ||
@pattern("^[a-zA-Z0-9-]{3,24}$") | ||
@key("employeeName") | ||
@path | ||
@segment("employees") | ||
name: string; | ||
} | ||
model EmployeeProperties {} | ||
@armResourceOperations | ||
interface Employees { | ||
@armResourceDelete(Employee) | ||
delete is ArmResourceDeleteWithoutOkAsync<Employee, EmployeeProperties, LroHeaders = {}>; | ||
} | ||
` | ||
) | ||
.toEmitDiagnostics({ | ||
code: "@azure-tools/typespec-azure-resource-manager/lro-location-header", | ||
}); | ||
}); | ||
|
||
it("Emits a warning for custom 202 response that does not contain a Location header", async () => { | ||
await tester | ||
.expect( | ||
` | ||
@armProviderNamespace | ||
@useDependency(Azure.ResourceManager.Versions.v1_0_Preview_1) | ||
namespace Microsoft.Contoso; | ||
model Employee is ProxyResource<EmployeeProperties> { | ||
@doc("Name of employee") | ||
@pattern("^[a-zA-Z0-9-]{3,24}$") | ||
@key("employeeName") | ||
@path | ||
@segment("employees") | ||
name: string; | ||
} | ||
model EmployeeProperties {} | ||
@armResourceOperations | ||
interface Employees { | ||
@armResourceDelete(Employee) | ||
delete(): { @statusCode _: 202 }; | ||
} | ||
` | ||
) | ||
.toEmitDiagnostics({ | ||
code: "@azure-tools/typespec-azure-resource-manager/lro-location-header", | ||
}); | ||
}); | ||
|
||
it("Does not emit a warning for LRO 202 response that contains the Location response header", async () => { | ||
await tester | ||
.expect( | ||
` | ||
@armProviderNamespace | ||
@useDependency(Azure.ResourceManager.Versions.v1_0_Preview_1) | ||
namespace Microsoft.Contoso; | ||
model Employee is ProxyResource<EmployeeProperties> { | ||
@doc("Name of employee") | ||
@pattern("^[a-zA-Z0-9-]{3,24}$") | ||
@key("employeeName") | ||
@path | ||
@segment("employees") | ||
name: string; | ||
} | ||
model EmployeeProperties {} | ||
@armResourceOperations | ||
interface Employees { | ||
delete is ArmResourceDeleteWithoutOkAsync<Employee, EmployeeProperties>; | ||
}` | ||
) | ||
.toBeValid(); | ||
}); |