fix: ensure tags are handled by the generated clients

Signed-off-by: aramissennyeydd <aramis.sennyey@doordash.com>
This commit is contained in:
aramissennyeydd
2024-11-28 12:46:59 -05:00
parent 348cbdfa68
commit f1fe0d4e35
14 changed files with 117 additions and 77 deletions
@@ -22,7 +22,6 @@ import { FetchApi } from '../types/fetch';
import crossFetch from 'cross-fetch';
import { pluginId } from '../pluginId';
import * as parser from 'uri-template';
import { AnalyzeLocationRequest } from '../models/AnalyzeLocationRequest.model';
import { AnalyzeLocationResponse } from '../models/AnalyzeLocationResponse.model';
import { CreateLocation201Response } from '../models/CreateLocation201Response.model';
@@ -35,8 +34,8 @@ import { EntityFacetsResponse } from '../models/EntityFacetsResponse.model';
import { GetEntitiesByRefsRequest } from '../models/GetEntitiesByRefsRequest.model';
import { GetLocations200ResponseInner } from '../models/GetLocations200ResponseInner.model';
import { Location } from '../models/Location.model';
import { RefreshEntityRequest } from '../models/RefreshEntityRequest.model';
import { ValidateEntityRequest } from '../models/ValidateEntityRequest.model';
import { RefreshEntityRequest } from '../models/RefreshEntityRequest.model';
/**
* Wraps the Response type to convey a type on the json call.
@@ -55,7 +54,6 @@ export type TypedResponse<T> = Omit<Response, 'json'> & {
export interface RequestOptions {
token?: string;
}
/**
* @public
*/
@@ -186,18 +184,17 @@ export type GetLocations = {};
/**
* @public
*/
export type RefreshEntity = {
body: RefreshEntityRequest;
export type ValidateEntity = {
body: ValidateEntityRequest;
};
/**
* @public
*/
export type ValidateEntity = {
body: ValidateEntityRequest;
export type RefreshEntity = {
body: RefreshEntityRequest;
};
/**
* no description
* @public
*/
export class DefaultApiClient {
@@ -601,31 +598,6 @@ export class DefaultApiClient {
});
}
/**
* Refresh the entity related to entityRef.
* @param refreshEntityRequest -
*/
public async refreshEntity(
// @ts-ignore
request: RefreshEntity,
options?: RequestOptions,
): Promise<TypedResponse<void>> {
const baseUrl = await this.discoveryApi.getBaseUrl(pluginId);
const uriTemplate = `/refresh`;
const uri = parser.parse(uriTemplate).expand({});
return await this.fetchApi.fetch(`${baseUrl}${uri}`, {
headers: {
'Content-Type': 'application/json',
...(options?.token && { Authorization: `Bearer ${options?.token}` }),
},
method: 'POST',
body: JSON.stringify(request.body),
});
}
/**
* Validate that a passed in entity has no errors in schema.
* @param validateEntityRequest -
@@ -650,4 +622,29 @@ export class DefaultApiClient {
body: JSON.stringify(request.body),
});
}
/**
* Refresh the entity related to entityRef.
* @param refreshEntityRequest -
*/
public async refreshEntity(
// @ts-ignore
request: RefreshEntity,
options?: RequestOptions,
): Promise<TypedResponse<void>> {
const baseUrl = await this.discoveryApi.getBaseUrl(pluginId);
const uriTemplate = `/refresh`;
const uri = parser.parse(uriTemplate).expand({});
return await this.fetchApi.fetch(`${baseUrl}${uri}`, {
headers: {
'Content-Type': 'application/json',
...(options?.token && { Authorization: `Bearer ${options?.token}` }),
},
method: 'POST',
body: JSON.stringify(request.body),
});
}
}
@@ -14,4 +14,4 @@
* limitations under the License.
*/
export * from './DefaultApi.client';
export * from './Api.client';
@@ -21,11 +21,13 @@ import {
OUTPUT_PATH,
} from '../../../../../lib/openapi/constants';
import { paths as cliPaths } from '../../../../../lib/paths';
import { mkdirpSync } from 'fs-extra';
import fs from 'fs-extra';
import { exec } from '../../../../../lib/exec';
import { resolvePackagePath } from '@backstage/backend-plugin-api';
import { getPathToCurrentOpenApiSpec } from '../../../../../lib/openapi/helpers';
import {
getPathToCurrentOpenApiSpec,
toGeneratorAdditionalProperties,
} from '../../../../../lib/openapi/helpers';
async function generate(
outputDirectory: string,
@@ -37,12 +39,8 @@ async function generate(
outputDirectory,
OUTPUT_PATH,
);
const additionalProperties = clientAdditionalProperties
? `--additional-properties=${clientAdditionalProperties}`
: '';
mkdirpSync(resolvedOutputDirectory);
await fs.mkdirp(resolvedOutputDirectory);
await fs.emptyDir(resolvedOutputDirectory);
await fs.writeFile(
resolve(resolvedOutputDirectory, '.openapi-generator-ignore'),
@@ -67,7 +65,11 @@ async function generate(
),
'--generator-key',
'v3.0',
additionalProperties,
'--additional-properties',
toGeneratorAdditionalProperties({
defaultValue: { useTags: false },
initialValue: clientAdditionalProperties,
}),
],
{
signal: abortSignal?.signal,
@@ -30,6 +30,7 @@ import { resolvePackagePath } from '@backstage/backend-plugin-api';
import {
getPathToCurrentOpenApiSpec,
getRelativePathToFile,
toGeneratorAdditionalProperties,
} from '../../../../../lib/openapi/helpers';
async function generateSpecFile() {
@@ -86,7 +87,7 @@ async function generate(abortSignal?: AbortController) {
const resolvedOpenapiPath = await getPathToCurrentOpenApiSpec();
const resolvedOutputDirectory = await getRelativePathToFile(OUTPUT_PATH);
await fs.mkdirp(resolvedOutputDirectory);
await fs.emptyDir(resolvedOutputDirectory);
await fs.writeFile(
resolve(resolvedOutputDirectory, '.openapi-generator-ignore'),
@@ -111,6 +112,8 @@ async function generate(abortSignal?: AbortController) {
),
'--generator-key',
'v3.0',
'--additional-properties',
toGeneratorAdditionalProperties({ defaultValue: { useTags: false } }),
],
{
maxBuffer: Number.MAX_VALUE,
@@ -51,3 +51,23 @@ export async function loadAndValidateOpenApiYaml(path: string) {
await Parser.validate(cloneDeep(yaml) as any);
return yaml;
}
export function toGeneratorAdditionalProperties({
initialValue,
defaultValue,
}: {
initialValue?: string;
defaultValue?: Record<string, any>;
}) {
const items = initialValue?.split(',') ?? [];
const parsed = items.reduce(
(acc, item) => {
const [key, value] = item.split('=');
return { ...acc, [key]: value };
},
{ ...defaultValue },
);
return Object.entries(parsed)
.map(([key, value]) => `${key}=${value}`)
.join(',');
}
@@ -1,10 +1,6 @@
templateDir: templates/typescript-backstage-client
files:
api.mustache:
templateType: API
# For some reason, they check for destinationFilename differences. We have to change the ending to override the file.
destinationFilename: .client.ts
model.mustache:
templateType: Model
destinationFilename: .model.ts
@@ -32,6 +28,9 @@ files:
apis/index.mustache:
templateType: SupportingFiles
destinationFilename: apis/index.ts
apis/api-all.mustache:
templateType: SupportingFiles
destinationFilename: apis/Api.client.ts
pluginId.mustache:
templateType: SupportingFiles
destinationFilename: pluginId.ts
@@ -4,10 +4,12 @@ import { FetchApi } from '../types/fetch';
import crossFetch from 'cross-fetch';
import {pluginId} from '../pluginId';
import * as parser from 'uri-template';
{{#apiInfo}}
{{#apis}}
{{#imports}}
import { {{classname}} } from '{{filename}}.model{{importFileExtension}}';
{{/imports}}
{{/apis}}
/**
* Wraps the Response type to convey a type on the json call.
@@ -18,8 +20,6 @@ export type TypedResponse<T> = Omit<Response, 'json'> & {
json: () => Promise<T>;
};
{{#operations}}
/**
* Options you can pass into a request for additional information.
*
@@ -28,7 +28,8 @@ export type TypedResponse<T> = Omit<Response, 'json'> & {
export interface RequestOptions {
token?: string;
}
{{#apis}}
{{#operations}}
{{#operation}}
/**
* @public
@@ -62,12 +63,13 @@ export type {{#lambda.pascalcase}}{{nickname}}{{/lambda.pascalcase}} = {
{{/hasHeaderParams}}
}
{{/operation}}
{{/operations}}
{{/apis}}
/**
* {{{description}}}{{^description}}no description{{/description}}
* @public
*/
export class {{classname}}Client {
export class DefaultApiClient {
private readonly discoveryApi: DiscoveryApi;
private readonly fetchApi: FetchApi;
@@ -79,6 +81,9 @@ export class {{classname}}Client {
this.fetchApi = options.fetchApi || { fetch: crossFetch };
}
{{#apis}}
{{#operations}}
{{#operation}}
/**
{{#notes}}
@@ -123,5 +128,7 @@ export class {{classname}}Client {
}
{{/operation}}
{{/operations}}
{{/apis}}
}
{{/operations}}
{{/apiInfo}}
@@ -1,3 +1,3 @@
//
export * from './DefaultApi.client';
export * from './Api.client';
@@ -1,13 +1,12 @@
templateDir: templates/typescript-backstage-server
files:
api.mustache:
templateType: API
# For some reason, they check for destinationFilename differences. We have to change the ending to override the file.
destinationFilename: .server.ts
apis/index.mustache:
templateType: SupportingFiles
destinationFilename: apis/index.ts
apis/api-all.mustache:
templateType: SupportingFiles
destinationFilename: apis/Api.server.ts
index.mustache:
templateType: SupportingFiles
destinationFilename: index.ts
@@ -1,4 +1,7 @@
//
{{>licenseInfo}}
{{#apiInfo}}
{{#apis}}
{{#imports}}
import { {{classname}} } from '{{filename}}.model{{importFileExtension}}';
{{/imports}}
@@ -39,16 +42,19 @@ export type {{#lambda.pascalcase}}{{nickname}}{{/lambda.pascalcase}} = {
response: {{#responses}} {{{dataType}}}{{^dataType}}void{{/dataType}} {{^-last}} | {{/-last}} {{/responses}}
}
{{/operation}}
/**
* {{{description}}}{{^description}}no description{{/description}}
*/
{{/operations}}
{{/apis}}
export type EndpointMap = {
{{#apis}}
{{#operations}}
{{#operation}}
'#{{#lambda.lowercase}}{{httpMethod}}{{/lambda.lowercase}}|{{path}}': {{#lambda.pascalcase}}{{nickname}}{{/lambda.pascalcase}},
{{/operation}}
{{/operations}}
{{/apis}}
}
{{/operations}}
{{/apiInfo}}
@@ -1,3 +1,4 @@
//
{{>licenseInfo}}
export * from './DefaultApi.server';
export * from './Api.server'
@@ -14,6 +14,8 @@
* limitations under the License.
*/
//
// ******************************************************************
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
// ******************************************************************
@@ -29,7 +31,6 @@ import { EntityFacetsResponse } from '../models/EntityFacetsResponse.model';
import { GetEntitiesByRefsRequest } from '../models/GetEntitiesByRefsRequest.model';
import { GetLocations200ResponseInner } from '../models/GetLocations200ResponseInner.model';
import { Location } from '../models/Location.model';
import { RefreshEntityRequest } from '../models/RefreshEntityRequest.model';
import { ValidateEntity400Response } from '../models/ValidateEntity400Response.model';
import { ValidateEntityRequest } from '../models/ValidateEntityRequest.model';
@@ -175,13 +176,6 @@ export type GetLocationByEntity = {
export type GetLocations = {
response: Array<GetLocations200ResponseInner> | Error;
};
/**
* @public
*/
export type RefreshEntity = {
body: RefreshEntityRequest;
response: void | Error | Error;
};
/**
* @public
*/
@@ -189,10 +183,15 @@ export type ValidateEntity = {
body: ValidateEntityRequest;
response: void | ValidateEntity400Response;
};
import { RefreshEntityRequest } from '../models/RefreshEntityRequest.model';
/**
* no description
* @public
*/
export type RefreshEntity = {
body: RefreshEntityRequest;
response: void | Error | Error;
};
export type EndpointMap = {
'#post|/analyze-location': AnalyzeLocation;
@@ -223,7 +222,7 @@ export type EndpointMap = {
'#get|/locations': GetLocations;
'#post|/refresh': RefreshEntity;
'#post|/validate-entity': ValidateEntity;
'#post|/refresh': RefreshEntity;
};
@@ -14,4 +14,10 @@
* limitations under the License.
*/
export * from './DefaultApi.server';
//
// ******************************************************************
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
// ******************************************************************
export * from './Api.server';
@@ -788,6 +788,7 @@ export const spec = {
'/refresh': {
post: {
operationId: 'RefreshEntity',
tags: ['Entity'],
description: 'Refresh the entity related to entityRef.',
responses: {
'200': {