Repository navigation
Add Spector coverage for LRO client metadata #5740
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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,7 @@ | ||
| --- | ||
| changeKind: feature | ||
| packages: | ||
| - "@azure-tools/azure-http-specs" | ||
| --- | ||
|
|
||
| Add service-shaped Azure HTTP scenarios for LRO operation ID extraction and nested typed status metadata. |
This file contains hidden or 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
125 changes: 125 additions & 0 deletions
125
packages/azure-http-specs/specs/azure/client-generator-core/lro/metadata/main.tsp
This file contains hidden or 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,125 @@ | ||
| import "@azure-tools/typespec-azure-core"; | ||
| import "@azure-tools/typespec-client-generator-core"; | ||
| import "@typespec/spector"; | ||
| import "@typespec/versioning"; | ||
|
|
||
| using Azure.ClientGenerator.Core; | ||
| using Azure.Core; | ||
| using Spector; | ||
| using TypeSpec.Http; | ||
| using TypeSpec.Versioning; | ||
|
|
||
| @doc("Defines service-shaped HTTP flows for testing client LRO metadata.") | ||
| @scenarioService( | ||
| "/azure/client-generator-core/lro/metadata", | ||
| { | ||
| versioned: Versions, | ||
| } | ||
| ) | ||
| @global.Azure.ClientGenerator.Core.clientNamespace( | ||
| "azure.clientgenerator.core.lro.metadata", | ||
| "java" | ||
| ) | ||
| @global.Azure.ClientGenerator.Core.clientNamespace( | ||
| "specs.azure.clientgenerator.core.lro.metadata", | ||
| "python" | ||
| ) | ||
| namespace _Specs_.Azure.ClientGenerator.Core.Lro.Metadata; | ||
|
|
||
| enum Versions { | ||
| v2022_12_01_preview: "2022-12-01-preview", | ||
| } | ||
|
|
||
| model DocumentAnalysisStatus { | ||
| id: string; | ||
| status: global.Azure.Core.Foundations.OperationState; | ||
| result?: DocumentAnalysisResult; | ||
| } | ||
|
|
||
| model DocumentAnalysisResult { | ||
| summary: string; | ||
| } | ||
|
|
||
| @route("/documentModels/{modelId}/analyzeResults/{resultId}") | ||
| @get | ||
| op getDocumentAnalysisStatus( | ||
| @path modelId: string, | ||
|
|
||
| @lroOperationId | ||
| @path | ||
| resultId: string, | ||
|
|
||
| ...global.Azure.Core.Foundations.ApiVersionParameter, | ||
| ): DocumentAnalysisStatus; | ||
|
|
||
| @scenario | ||
| @scenarioDoc(""" | ||
| Declares a Document Intelligence-shaped flow for emitter tests of | ||
| `@lroOperationId` on the status-monitor operation ID path parameter. | ||
|
|
||
| The service returns the complete opaque status-monitor URI, including its API-version query. | ||
| Polling must continue with that URI. Emitter-specific client tests can separately assert that | ||
| the server-issued ID `analysis-123` is exposed from the final non-empty path segment. | ||
|
|
||
| Expected calls: | ||
| - POST `/documentModels/document-1:analyze` returns 202 with | ||
| `Operation-Location: {endpoint}/documentModels/document-1/analyzeResults/analysis-123?api-version=2022-12-01-preview` | ||
| - GET that exact status-monitor URI returns a running status | ||
| - GET that exact status-monitor URI returns a succeeded status with the analysis result | ||
| """) | ||
| @route("/documentModels/{modelId}:analyze") | ||
| @post | ||
| @pollingOperation(getDocumentAnalysisStatus) | ||
| op analyze( | ||
| @path modelId: string, | ||
| ...global.Azure.Core.Foundations.ApiVersionParameter, | ||
| ): global.Azure.Core.Foundations.AcceptedResponse & | ||
| global.Azure.Core.Foundations.LongRunningStatusLocation<DocumentAnalysisStatus>; | ||
|
|
||
| model UsageDetails { | ||
| documentPagesBasic?: int32; | ||
| tokens?: Record<int32>; | ||
| } | ||
|
|
||
| model ContentAnalysisStatus { | ||
| id: string; | ||
| status: global.Azure.Core.Foundations.OperationState; | ||
|
|
||
| @lroStatusMetadata | ||
| usage?: UsageDetails; | ||
|
|
||
| result?: ContentAnalysisResult; | ||
| } | ||
|
|
||
| model ContentAnalysisResult { | ||
| contents: string; | ||
| } | ||
|
|
||
| @route("/analyzerResults/{operationId}") | ||
| @get | ||
| op getContentAnalysisStatus( | ||
| @path operationId: string, | ||
| ...global.Azure.Core.Foundations.ApiVersionParameter, | ||
| ): ContentAnalysisStatus; | ||
|
|
||
| @scenario | ||
| @scenarioDoc(""" | ||
| Declares a Content Understanding-shaped flow for emitter tests of property-level | ||
| `@lroStatusMetadata`. | ||
|
|
||
| The optional `usage` value is a typed nested model with optional numeric fields and a token | ||
| dictionary. Emitter-specific client tests can assert the generated public type and runtime value. | ||
|
|
||
| Expected calls: | ||
| - POST `/analyzers/analyzer-1:analyze` returns 202 with a status-monitor URI | ||
| - GET the status-monitor URI returns a running status with nested `usage` | ||
| - GET the status-monitor URI returns a succeeded status with nested `usage` and the result | ||
| """) | ||
| @route("/analyzers/{analyzerId}:analyze") | ||
| @post | ||
| @pollingOperation(getContentAnalysisStatus) | ||
| op analyzeWithUsage( | ||
| @path analyzerId: string, | ||
| ...global.Azure.Core.Foundations.ApiVersionParameter, | ||
| ): global.Azure.Core.Foundations.AcceptedResponse & | ||
| global.Azure.Core.Foundations.LongRunningStatusLocation<ContentAnalysisStatus>; |
214 changes: 214 additions & 0 deletions
214
packages/azure-http-specs/specs/azure/client-generator-core/lro/metadata/mockapi.ts
This file contains hidden or 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,214 @@ | ||
| import { | ||
| dyn, | ||
| dynItem, | ||
| json, | ||
| MockRequest, | ||
| passOnSuccess, | ||
| type ScenarioMockApi, | ||
| } from "@typespec/spec-api"; | ||
|
|
||
| export const Scenarios: Record<string, ScenarioMockApi> = {}; | ||
|
|
||
| const apiVersion = "2022-12-01-preview"; | ||
| let analyzePollCount = 0; | ||
| let analyzeWithUsagePollCount = 0; | ||
|
|
||
| Scenarios.Azure_ClientGenerator_Core_Lro_Metadata_analyze = passOnSuccess([ | ||
| { | ||
| uri: "/azure/client-generator-core/lro/metadata/documentModels/document-1:analyze", | ||
| method: "post", | ||
| request: { | ||
| query: { | ||
| "api-version": apiVersion, | ||
| }, | ||
| }, | ||
| response: { | ||
| status: 202, | ||
| headers: { | ||
| "operation-location": dyn`${dynItem("baseUrl")}/azure/client-generator-core/lro/metadata/documentModels/document-1/analyzeResults/analysis-123?api-version=2022-12-01-preview`, | ||
| }, | ||
| }, | ||
| handler: (req: MockRequest) => { | ||
| req.expect.containsQueryParam("api-version", apiVersion); | ||
| analyzePollCount = 0; | ||
| return { | ||
| status: 202, | ||
| headers: { | ||
| "operation-location": `${req.baseUrl}/azure/client-generator-core/lro/metadata/documentModels/document-1/analyzeResults/analysis-123?api-version=${apiVersion}`, | ||
| }, | ||
| }; | ||
| }, | ||
| kind: "MockApiDefinition", | ||
| }, | ||
| { | ||
| uri: "/azure/client-generator-core/lro/metadata/documentModels/document-1/analyzeResults/analysis-123", | ||
| method: "get", | ||
| request: { | ||
| query: { | ||
| "api-version": apiVersion, | ||
| }, | ||
| }, | ||
| response: { | ||
| status: 200, | ||
| body: json({ | ||
| id: "analysis-123", | ||
| status: "Running", | ||
| }), | ||
| }, | ||
| handler: analyzeLroHandler, | ||
| kind: "MockApiDefinition", | ||
| }, | ||
| { | ||
| uri: "/azure/client-generator-core/lro/metadata/documentModels/document-1/analyzeResults/analysis-123", | ||
| method: "get", | ||
| request: { | ||
| query: { | ||
| "api-version": apiVersion, | ||
| }, | ||
| }, | ||
| response: { | ||
| status: 200, | ||
| body: json({ | ||
| id: "analysis-123", | ||
| status: "Succeeded", | ||
| result: { | ||
| summary: "The document contains three sections.", | ||
| }, | ||
| }), | ||
| }, | ||
| handler: analyzeLroHandler, | ||
| kind: "MockApiDefinition", | ||
| }, | ||
| ]); | ||
|
|
||
| Scenarios.Azure_ClientGenerator_Core_Lro_Metadata_analyzeWithUsage = passOnSuccess([ | ||
| { | ||
| uri: "/azure/client-generator-core/lro/metadata/analyzers/analyzer-1:analyze", | ||
| method: "post", | ||
| request: { | ||
| query: { | ||
| "api-version": apiVersion, | ||
| }, | ||
| }, | ||
| response: { | ||
| status: 202, | ||
| headers: { | ||
| "operation-location": dyn`${dynItem("baseUrl")}/azure/client-generator-core/lro/metadata/analyzerResults/usage-123?api-version=2022-12-01-preview`, | ||
| }, | ||
| }, | ||
| handler: (req: MockRequest) => { | ||
| req.expect.containsQueryParam("api-version", apiVersion); | ||
| analyzeWithUsagePollCount = 0; | ||
| return { | ||
| status: 202, | ||
| headers: { | ||
| "operation-location": `${req.baseUrl}/azure/client-generator-core/lro/metadata/analyzerResults/usage-123?api-version=${apiVersion}`, | ||
| }, | ||
| }; | ||
| }, | ||
| kind: "MockApiDefinition", | ||
| }, | ||
| { | ||
| uri: "/azure/client-generator-core/lro/metadata/analyzerResults/usage-123", | ||
| method: "get", | ||
| request: { | ||
| query: { | ||
| "api-version": apiVersion, | ||
| }, | ||
| }, | ||
| response: { | ||
| status: 200, | ||
| body: json({ | ||
| id: "usage-123", | ||
| status: "Running", | ||
| usage: { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would we want a test without usage being returned? |
||
| documentPagesBasic: 1, | ||
| tokens: { | ||
| input: 10, | ||
| }, | ||
| }, | ||
| }), | ||
| }, | ||
| handler: analyzeWithUsageLroHandler, | ||
| kind: "MockApiDefinition", | ||
| }, | ||
| { | ||
| uri: "/azure/client-generator-core/lro/metadata/analyzerResults/usage-123", | ||
| method: "get", | ||
| request: { | ||
| query: { | ||
| "api-version": apiVersion, | ||
| }, | ||
| }, | ||
| response: { | ||
| status: 200, | ||
| body: json({ | ||
| id: "usage-123", | ||
| status: "Succeeded", | ||
| usage: { | ||
| documentPagesBasic: 1, | ||
| tokens: { | ||
| input: 10, | ||
| output: 4, | ||
| }, | ||
| }, | ||
| result: { | ||
| contents: "The document contains three sections.", | ||
| }, | ||
| }), | ||
| }, | ||
| handler: analyzeWithUsageLroHandler, | ||
| kind: "MockApiDefinition", | ||
| }, | ||
| ]); | ||
|
|
||
| function analyzeLroHandler(req: MockRequest) { | ||
| req.expect.containsQueryParam("api-version", apiVersion); | ||
| const response = | ||
| analyzePollCount > 0 | ||
| ? { | ||
| id: "analysis-123", | ||
| status: "Succeeded", | ||
| result: { | ||
| summary: "The document contains three sections.", | ||
| }, | ||
| } | ||
| : { | ||
| id: "analysis-123", | ||
| status: "Running", | ||
| }; | ||
| analyzePollCount += 1; | ||
| return { status: 200, body: json(response) }; | ||
| } | ||
|
|
||
| function analyzeWithUsageLroHandler(req: MockRequest) { | ||
| req.expect.containsQueryParam("api-version", apiVersion); | ||
| const response = | ||
| analyzeWithUsagePollCount > 0 | ||
| ? { | ||
| id: "usage-123", | ||
| status: "Succeeded", | ||
| usage: { | ||
| documentPagesBasic: 1, | ||
| tokens: { | ||
| input: 10, | ||
| output: 4, | ||
| }, | ||
| }, | ||
| result: { | ||
| contents: "The document contains three sections.", | ||
| }, | ||
| } | ||
| : { | ||
| id: "usage-123", | ||
| status: "Running", | ||
| usage: { | ||
| documentPagesBasic: 1, | ||
| tokens: { | ||
| input: 10, | ||
| }, | ||
| }, | ||
| }; | ||
| analyzeWithUsagePollCount += 1; | ||
| return { status: 200, body: json(response) }; | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
body should not have this
id: "analysis-123"(ContentAnalysisStatusmodel too)The problem of Document Intelligence is that they do not follow the guideline and did not have this
id.If they had this
id,@operationIdis not needed at all.They just want to extract the equivalent of this
idfromoperation-locationheader (to call anothergetAnalyzeResultPdf/getAnalyzeResultFigureAPI).That is the reason they added/customized the
getResultId()in Java that parse the value ofoperation-locationheader and return the last segment.AKA, this is not used for polling related thing. It is only about "extract" the non-exist
idfromoperation-locationheader.