mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-07-15 00:14:53 +01:00
More updates
This commit is contained in:
parent
9db8d11801
commit
cfe9bd8303
21
package-lock.json
generated
21
package-lock.json
generated
@ -7582,6 +7582,27 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.1.tgz",
|
||||||
"integrity": "sha512-TJtwsqZ39pqcljJpajeoofYRfeZ7/I/OMUQ5pR4q5wOKf2ocrUvBAZUMhWsOvKx3dVc/aaV5GluBivt0sWqA5A=="
|
"integrity": "sha512-TJtwsqZ39pqcljJpajeoofYRfeZ7/I/OMUQ5pR4q5wOKf2ocrUvBAZUMhWsOvKx3dVc/aaV5GluBivt0sWqA5A=="
|
||||||
},
|
},
|
||||||
|
"@types/node-fetch": {
|
||||||
|
"version": "2.5.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz",
|
||||||
|
"integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==",
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"form-data": "^3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"form-data": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==",
|
||||||
|
"requires": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/normalize-package-data": {
|
"@types/normalize-package-data": {
|
||||||
"version": "2.4.0",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
"@nteract/transform-vega": "7.0.6",
|
"@nteract/transform-vega": "7.0.6",
|
||||||
"@octokit/rest": "17.9.2",
|
"@octokit/rest": "17.9.2",
|
||||||
"@phosphor/widgets": "1.9.3",
|
"@phosphor/widgets": "1.9.3",
|
||||||
|
"@types/node-fetch": "2.5.7",
|
||||||
"@uifabric/react-cards": "0.109.110",
|
"@uifabric/react-cards": "0.109.110",
|
||||||
"@uifabric/styling": "7.13.7",
|
"@uifabric/styling": "7.13.7",
|
||||||
"abort-controller": "3.0.0",
|
"abort-controller": "3.0.0",
|
||||||
@ -188,7 +189,8 @@
|
|||||||
"build:contracts": "npm run compile:contracts",
|
"build:contracts": "npm run compile:contracts",
|
||||||
"strictEligibleFiles": "node ./strict-migration-tools/index.js",
|
"strictEligibleFiles": "node ./strict-migration-tools/index.js",
|
||||||
"autoAddStrictEligibleFiles": "node ./strict-migration-tools/autoAdd.js",
|
"autoAddStrictEligibleFiles": "node ./strict-migration-tools/autoAdd.js",
|
||||||
"compile:fullStrict": "tsc -p ./tsconfig.json --strictNullChecks"
|
"compile:fullStrict": "tsc -p ./tsconfig.json --strictNullChecks",
|
||||||
|
"generateARMClient": "ts-node --compiler-options '{\"module\":\"commonjs\"}' utils/armClientGenerator/generator.ts"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -1,33 +1,51 @@
|
|||||||
/// <reference types="node" />
|
/// <reference types="node" />
|
||||||
|
import { writeFileSync } from "fs";
|
||||||
|
import fetch from "node-fetch";
|
||||||
|
|
||||||
const { writeFileSync } = require("fs");
|
/*
|
||||||
const schema = require("./schema.json");
|
Open API TypeScript Client Generator
|
||||||
|
|
||||||
const file: string[] = [""];
|
This is a quickly made bespoke Open API client generator.
|
||||||
|
It is not designed to handle the full OpenAPI spec.
|
||||||
|
Many other more general purpose generators exist, but their output is very verbose and overly complex for our use case.
|
||||||
|
But it does work well enough to generate a fully typed tree-shakeable client for the Cosmos resource provider.
|
||||||
|
Results of this file should be checked into the repo.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Array of strings to use for eventual output
|
||||||
|
const output: string[] = [""];
|
||||||
|
|
||||||
|
const schemaURL =
|
||||||
|
"https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/specification/cosmos-db/resource-manager/Microsoft.DocumentDB/preview/2020-06-01-preview/cosmos-db.json";
|
||||||
|
|
||||||
|
// Buckets for grouping operations based on their name
|
||||||
const namespaces: { [key: string]: string[] } = {};
|
const namespaces: { [key: string]: string[] } = {};
|
||||||
|
|
||||||
|
// Mapping for OpenAPI types to TypeScript types
|
||||||
const propertyMap: { [key: string]: string } = {
|
const propertyMap: { [key: string]: string } = {
|
||||||
integer: "number"
|
integer: "number"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Converts a Open API reference: "#/definitions/Foo" to a type name: Foo
|
||||||
function refToType(path: string | undefined) {
|
function refToType(path: string | undefined) {
|
||||||
// Handles refs pointing to other files. We don't support that yet.
|
// References must be in the same file. Bail to `unknown` types for remote references
|
||||||
if (path && path.startsWith("#")) {
|
if (path && path.startsWith("#")) {
|
||||||
return path.split("/").pop();
|
return path.split("/").pop();
|
||||||
}
|
}
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Converts "Something_Foo" -> "somethingFoo"
|
||||||
function camelize(str: string) {
|
function camelize(str: string) {
|
||||||
return str
|
return str
|
||||||
.replace(/(?:^\w|[A-Z]|\b\w)/g, function(word: any, index: any) {
|
.replace(/(?:^\w|[A-Z]|\b\w)/g, function(word: string, index: number) {
|
||||||
return index === 0 ? word.toLowerCase() : word.toUpperCase();
|
return index === 0 ? word.toLowerCase() : word.toUpperCase();
|
||||||
})
|
})
|
||||||
.replace(/\s+/g, "");
|
.replace(/\s+/g, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
function bodyParam(parameter: any) {
|
// Converts a body paramter to the equivalent typescript function parameter type
|
||||||
|
function bodyParam(parameter: { schema: { $ref: string } }) {
|
||||||
if (!parameter) {
|
if (!parameter) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -35,18 +53,22 @@ function bodyParam(parameter: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
function parametersFromPath(path: any) {
|
function parametersFromPath(path: string) {
|
||||||
// TODO: Remove any. String.matchAll is a real thing.
|
// TODO: Remove any. String.matchAll is a real thing.
|
||||||
const matches = path.matchAll(/{(\w+)}/g);
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const matches = (path as any).matchAll(/{(\w+)}/g);
|
||||||
return Array.from(matches)
|
return Array.from(matches)
|
||||||
.map((match: string[]) => `${match[1]}: string`)
|
.map((match: string[]) => `${match[1]}: string`)
|
||||||
.join(",\n");
|
.join(",\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
function responseType(operation: any) {
|
type Operation = { responses: { [key: string]: { schema: { $ref: string } } } };
|
||||||
|
|
||||||
|
// Converts OpenAPI response definition to TypeScript return type. Uses unions if possible. Bails to unknown
|
||||||
|
function responseType(operation: Operation) {
|
||||||
if (operation.responses) {
|
if (operation.responses) {
|
||||||
return Object.keys(operation.responses)
|
return Object.keys(operation.responses)
|
||||||
.map((responseCode: any) => {
|
.map((responseCode: string) => {
|
||||||
if (!operation.responses[responseCode].schema) {
|
if (!operation.responses[responseCode].schema) {
|
||||||
return "void";
|
return "void";
|
||||||
}
|
}
|
||||||
@ -58,40 +80,44 @@ function responseType(operation: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
for (const interface in schema.definitions) {
|
const response = await fetch(schemaURL);
|
||||||
const properties = schema.definitions[interface].properties;
|
const schema = await response.json();
|
||||||
|
|
||||||
|
// STEP 1: Convert all definitions to TypeScript types and interfaces
|
||||||
|
for (const definition in schema.definitions) {
|
||||||
|
const properties = schema.definitions[definition].properties;
|
||||||
if (properties) {
|
if (properties) {
|
||||||
if (schema.definitions[interface].allOf) {
|
if (schema.definitions[definition].allOf) {
|
||||||
const baseTypes = schema.definitions[interface].allOf
|
const baseTypes = schema.definitions[definition].allOf
|
||||||
.map((allof: { $ref: string }) => refToType(allof.$ref))
|
.map((allof: { $ref: string }) => refToType(allof.$ref))
|
||||||
.join(" & ");
|
.join(" & ");
|
||||||
file.push(`type ${interface} = ${baseTypes} & {`);
|
output.push(`type ${definition} = ${baseTypes} & {`);
|
||||||
} else {
|
} else {
|
||||||
file.push(`interface ${interface} {`);
|
output.push(`interface ${definition} {`);
|
||||||
}
|
}
|
||||||
for (const prop in schema.definitions[interface].properties) {
|
for (const prop in schema.definitions[definition].properties) {
|
||||||
const property = schema.definitions[interface].properties[prop];
|
const property = schema.definitions[definition].properties[prop];
|
||||||
if (property) {
|
if (property) {
|
||||||
if (property.$ref) {
|
if (property.$ref) {
|
||||||
const type = refToType(property.$ref);
|
const type = refToType(property.$ref);
|
||||||
file.push(`
|
output.push(`
|
||||||
/* ${property.description} */
|
/* ${property.description} */
|
||||||
${property.readOnly ? "readonly " : ""}${prop}: ${type}
|
${property.readOnly ? "readonly " : ""}${prop}: ${type}
|
||||||
`);
|
`);
|
||||||
} else if (property.type === "array") {
|
} else if (property.type === "array") {
|
||||||
const type = refToType(property.items.$ref);
|
const type = refToType(property.items.$ref);
|
||||||
file.push(`
|
output.push(`
|
||||||
/* ${property.description} */
|
/* ${property.description} */
|
||||||
${property.readOnly ? "readonly " : ""}${prop}: ${type}[]
|
${property.readOnly ? "readonly " : ""}${prop}: ${type}[]
|
||||||
`);
|
`);
|
||||||
} else if (property.type === "object") {
|
} else if (property.type === "object") {
|
||||||
const type = refToType(property.$ref);
|
const type = refToType(property.$ref);
|
||||||
file.push(`
|
output.push(`
|
||||||
/* ${property.description} */
|
/* ${property.description} */
|
||||||
${property.readOnly ? "readonly " : ""}${prop}: ${type}
|
${property.readOnly ? "readonly " : ""}${prop}: ${type}
|
||||||
`);
|
`);
|
||||||
} else {
|
} else {
|
||||||
file.push(`
|
output.push(`
|
||||||
/* ${property.description} */
|
/* ${property.description} */
|
||||||
${property.readOnly ? "readonly " : ""}${prop}: ${
|
${property.readOnly ? "readonly " : ""}${prop}: ${
|
||||||
propertyMap[property.type] ? propertyMap[property.type] : property.type
|
propertyMap[property.type] ? propertyMap[property.type] : property.type
|
||||||
@ -99,48 +125,50 @@ async function main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file.push(`}`);
|
output.push(`}`);
|
||||||
file.push("\n\n");
|
output.push("\n\n");
|
||||||
} else {
|
} else {
|
||||||
const definition = schema.definitions[interface];
|
const def = schema.definitions[definition];
|
||||||
if (definition.enum) {
|
if (def.enum) {
|
||||||
file.push(`
|
output.push(`
|
||||||
/* ${definition.description} */
|
/* ${def.description} */
|
||||||
type ${interface} = ${definition.enum.map((v: string) => `"${v}"`).join(" | ")}`);
|
type ${definition} = ${def.enum.map((v: string) => `"${v}"`).join(" | ")}`);
|
||||||
file.push("\n");
|
output.push("\n");
|
||||||
} else if (definition.type === "string") {
|
} else if (def.type === "string") {
|
||||||
file.push(`
|
output.push(`
|
||||||
/* ${definition.description} */
|
/* ${def.description} */
|
||||||
type ${interface} = string
|
type ${definition} = string
|
||||||
`);
|
`);
|
||||||
} else if (definition.type === "array") {
|
} else if (def.type === "array") {
|
||||||
const type = refToType(definition.items.$ref);
|
const type = refToType(def.items.$ref);
|
||||||
file.push(`
|
output.push(`
|
||||||
/* ${definition.description} */
|
/* ${def.description} */
|
||||||
type ${interface} = ${type}[]
|
type ${definition} = ${type}[]
|
||||||
`);
|
`);
|
||||||
} else if (definition.type === "object" && definition.additionalProperties) {
|
} else if (def.type === "object" && def.additionalProperties) {
|
||||||
file.push(`
|
output.push(`
|
||||||
/* ${definition.description} */
|
/* ${def.description} */
|
||||||
type ${interface} = { [key: string]: ${definition.additionalProperties.type}}
|
type ${definition} = { [key: string]: ${def.additionalProperties.type}}
|
||||||
`);
|
`);
|
||||||
} else if (definition.type === "object" && definition.allOf) {
|
} else if (def.type === "object" && def.allOf) {
|
||||||
const type = refToType(definition.allOf[0].$ref);
|
const type = refToType(def.allOf[0].$ref);
|
||||||
file.push(`
|
output.push(`
|
||||||
/* ${definition.description} */
|
/* ${def.description} */
|
||||||
type ${interface} = ${type}
|
type ${definition} = ${type}
|
||||||
`);
|
`);
|
||||||
} else {
|
} else {
|
||||||
console.log("UNHANDLED MODEL:", interface, schema.definitions[interface]);
|
console.log("UNHANDLED MODEL:", def, schema.definitions[def]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// STEP 2: Convert all paths and operations to simple fetch functions.
|
||||||
|
// Functions are grouped into objects based on resource types
|
||||||
for (const path in schema.paths) {
|
for (const path in schema.paths) {
|
||||||
for (const method in schema.paths[path]) {
|
for (const method in schema.paths[path]) {
|
||||||
const operation = schema.paths[path][method];
|
const operation = schema.paths[path][method];
|
||||||
const bodyParameter = operation.parameters.find(
|
const bodyParameter = operation.parameters.find(
|
||||||
(parameter: any) => parameter.in === "body" && parameter.required === true
|
(parameter: { in: string; required: boolean }) => parameter.in === "body" && parameter.required === true
|
||||||
);
|
);
|
||||||
const [namespace, operationName] = operation.operationId.split("_");
|
const [namespace, operationName] = operation.operationId.split("_");
|
||||||
if (namespaces[namespace] === undefined) {
|
if (namespaces[namespace] === undefined) {
|
||||||
@ -160,13 +188,14 @@ async function main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write all grouped fetch functions to objects
|
||||||
for (const namespace in namespaces) {
|
for (const namespace in namespaces) {
|
||||||
file.push(`export const ${namespace} = {`);
|
output.push(`export const ${namespace} = {`);
|
||||||
file.push(namespaces[namespace].join(",\n"));
|
output.push(namespaces[namespace].join(",\n"));
|
||||||
file.push(`}\n`);
|
output.push(`}\n`);
|
||||||
}
|
}
|
||||||
|
|
||||||
writeFileSync("./models.ts", file.join(""));
|
writeFileSync("./client.ts", output.join(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
main().catch(e => {
|
main().catch(e => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user