forked from Azure/azure-sdk-for-js
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request Azure#577 from chradek/pagination-take-2
Pagination support
- Loading branch information
Showing
21 changed files
with
2,658 additions
and
6 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
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,128 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
import { | ||
CodeModel, | ||
Operation, | ||
SchemaType, | ||
Parameter, | ||
StringSchema, | ||
Protocol, | ||
ParameterLocation | ||
} from "@azure-tools/codemodel"; | ||
import { cloneOperation } from "../utils/cloneOperation"; | ||
import { extractPaginationDetails } from "../utils/extractPaginationDetails"; | ||
import { getLanguageMetadata } from "../utils/languageHelpers"; | ||
|
||
/** | ||
* Normalizes the CodeModel based on available Azure extensions. | ||
* This may result in additional operations being inserted into the model. | ||
* @param codeModel The model that contains all the information required to generate a service API. | ||
*/ | ||
export function normalizeModelWithExtensions(codeModel: CodeModel) { | ||
addPageableMethods(codeModel); | ||
} | ||
|
||
/** | ||
* Adds the <operationName>Next method for each operation with an x-ms-pageable extension. | ||
* @param codeModel | ||
*/ | ||
function addPageableMethods(codeModel: CodeModel) { | ||
const operationGroups = codeModel.operationGroups; | ||
|
||
for (const operationGroup of operationGroups) { | ||
const operationGroupMetadata = getLanguageMetadata(operationGroup.language); | ||
const operations = operationGroup.operations.slice(); | ||
|
||
for (const operation of operations) { | ||
const paginationDetails = extractPaginationDetails(operation); | ||
const operationMetadata = getLanguageMetadata(operation.language); | ||
const operationName = operationMetadata.name; | ||
const operationDescription = operationMetadata.description; | ||
|
||
if (!paginationDetails || !paginationDetails.nextLinkName) { | ||
// The operation either doesn't support pagination or returns all items in a single page. | ||
// Therefore, it is not necessary to create a pageable method. | ||
continue; | ||
} | ||
|
||
const nextLinkOperationName = paginationDetails.nextLinkOperationName; | ||
if (!nextLinkOperationName) { | ||
// We don't know what the new operation name is. | ||
throw new Error( | ||
`Unable to determine the x-ms-pageable operationName for "${operationName}".` | ||
); | ||
} | ||
|
||
// Attempt to find the nextLinkOperationName in the code model. | ||
let nextLinkMethod = findOperation( | ||
codeModel, | ||
paginationDetails.group ?? operationGroupMetadata.name, | ||
nextLinkOperationName | ||
); | ||
|
||
if (nextLinkMethod) { | ||
// The operation to call to get subsequent pages already exists, so we don't need to create it. | ||
const metadata = getLanguageMetadata(nextLinkMethod.language); | ||
metadata.paging.isNextLinkMethod = true; | ||
continue; | ||
} | ||
|
||
// The "Next" operation doesn't exist, so we need to create it using current operation as a base. | ||
nextLinkMethod = cloneOperation( | ||
operation, | ||
nextLinkOperationName, | ||
operationDescription | ||
); | ||
|
||
const nextLinkMethodMetadata = getLanguageMetadata( | ||
nextLinkMethod.language | ||
); | ||
nextLinkMethodMetadata.paging.isNextLinkMethod = true; | ||
|
||
// Since this is a brand new operation, the nextLink will be a partial or absolute url. | ||
const nextLinkRequestProtocol = | ||
nextLinkMethod.request.protocol.http ?? new Protocol(); | ||
nextLinkRequestProtocol.path = "{nextLink}"; | ||
|
||
// Create the nextLink parameter. | ||
// This will appear as a required parameter to the "Next" operation. | ||
const httpProtocol = new Protocol(); | ||
httpProtocol.in = ParameterLocation.Path; | ||
const nextLinkParameter = new Parameter( | ||
"nextLink", | ||
`The nextLink from the previous successful call to the ${operationName} method.`, | ||
new StringSchema("string", ""), | ||
{ | ||
required: true, | ||
language: { | ||
default: { | ||
serializedName: "nextLink" | ||
} | ||
}, | ||
extensions: { | ||
"x-ms-skip-url-encoding": true | ||
}, | ||
protocol: { | ||
http: httpProtocol | ||
} | ||
} | ||
); | ||
nextLinkMethod.request.addParameter(nextLinkParameter); | ||
|
||
operationGroup.addOperation(nextLinkMethod); | ||
} | ||
} | ||
} | ||
|
||
function findOperation( | ||
codeModel: CodeModel, | ||
operationGroupName: string, | ||
operationName: string | ||
): Operation | undefined { | ||
const operationGroup = codeModel.getOperationGroup(operationGroupName); | ||
return operationGroup?.operations.find(operation => { | ||
const languageMetadata = getLanguageMetadata(operation.language); | ||
return languageMetadata.name === operationName; | ||
}); | ||
} |
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
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,36 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
import { Operation } from "@azure-tools/codemodel"; | ||
import { cloneDeep } from "lodash"; | ||
import { getLanguageMetadata } from "./languageHelpers"; | ||
|
||
/** | ||
* Clone an operation and overwrite the operation name and description. | ||
* @param operation | ||
* @param operationName | ||
* @param operationDescription | ||
*/ | ||
export function cloneOperation( | ||
operation: Operation, | ||
operationName: string, | ||
operationDescription: string | ||
) { | ||
const operationInitializer = cloneDeep(operation); | ||
// filter out methods | ||
for (const key of Object.keys(operationInitializer)) { | ||
if (typeof (operationInitializer as any)[key] === "function") { | ||
delete (operationInitializer as any)[key]; | ||
} | ||
} | ||
const newOperation = new Operation( | ||
operationName, | ||
operationDescription, | ||
operationInitializer | ||
); | ||
const operationMetadata = getLanguageMetadata(newOperation.language); | ||
operationMetadata.name = operationName; | ||
operationMetadata.description = operationName; | ||
|
||
return newOperation; | ||
} |
Oops, something went wrong.