From c4e68ed48841fb3ee0122d3ca0f708785dcf2440 Mon Sep 17 00:00:00 2001 From: Artem Derevnjuk Date: Thu, 27 Jan 2022 21:02:01 +0300 Subject: [PATCH] feat(oas): augment auto sampling errors with error location info --- .../oas/src/converter/DefaultConverter.ts | 100 ++++++++++++++---- 1 file changed, 79 insertions(+), 21 deletions(-) diff --git a/packages/oas/src/converter/DefaultConverter.ts b/packages/oas/src/converter/DefaultConverter.ts index b3b7b359..a0728956 100644 --- a/packages/oas/src/converter/DefaultConverter.ts +++ b/packages/oas/src/converter/DefaultConverter.ts @@ -125,6 +125,8 @@ export class DefaultConverter implements Converter { method: string ): PostData | null { const pathObj = spec.paths[path][method]; + // TODO: escape a reference token (i.g. path) + const jsonPointer = `/paths/${path}/${method}`; for (const param of pathObj.parameters || []) { if ( @@ -133,10 +135,18 @@ export class DefaultConverter implements Converter { typeof param.schema !== 'undefined' ) { try { - const data = this.sample(param.schema, { - spec, - skipReadOnly: true - }); + const data = this.sample( + param.schema, + { + skipReadOnly: true + }, + { + spec, + jsonPointer: `${jsonPointer}/parameters/${pathObj.parameters.indexOf( + param + )}/schema` + } + ); let consumes; @@ -174,10 +184,17 @@ export class DefaultConverter implements Converter { if (content[contentType] && content[contentType].schema) { const sampleContent = content[contentType]; - const data = this.sample(content[contentType].schema, { - spec, - skipReadOnly: true - }); + const data = this.sample( + content[contentType].schema, + { + skipReadOnly: true + }, + { + spec, + // TODO: escape a reference token (i.g. contentType) + jsonPointer: `${jsonPointer}/requestBody/content/${contentType}/schema` + } + ); return this.encodePayload(data, contentType, sampleContent.encoding); } @@ -337,17 +354,29 @@ export class DefaultConverter implements Converter { values: Record = {} ): QueryString[] { const queryStrings: QueryString[] = []; + const pathObj = spec.paths[path][method]; + // TODO: escape a reference token (i.g. path) + const jsonPointer = `/paths/${path}/${method}`; - if (typeof spec.paths[path][method].parameters === 'undefined') { + if (typeof pathObj.parameters === 'undefined') { return queryStrings; } - for (const param of spec.paths[path][method].parameters) { + for (const param of pathObj.parameters) { if ( typeof param.in !== 'undefined' && param.in.toLowerCase() === 'query' ) { - const data = this.sample(param.schema || param, { spec }); + const data = this.sample( + param.schema || param, + {}, + { + spec, + jsonPointer: `${jsonPointer}/parameters/${pathObj.parameters.indexOf( + param + )}/schema` + } + ); if (typeof values[param.name] !== 'undefined') { queryStrings.push({ @@ -385,8 +414,9 @@ export class DefaultConverter implements Converter { method: string ): Header[] { const headers: Header[] = []; - const pathObj = spec.paths[path][method]; + // TODO: escape a reference token (i.g. path) + const jsonPointer = `/paths/${path}/${method}`; // 'content-type' header: if (typeof pathObj.consumes !== 'undefined') { @@ -425,7 +455,16 @@ export class DefaultConverter implements Converter { typeof param.in !== 'undefined' && param.in.toLowerCase() === 'header' ) { - const data = this.sample(param.schema || param, { spec }); + const data = this.sample( + param.schema || param, + {}, + { + spec, + jsonPointer: `${jsonPointer}/parameters/${pathObj.parameters.indexOf( + param + )}/schema` + } + ); headers.push({ name: param.name.toLowerCase(), value: typeof data === 'object' ? JSON.stringify(data) : data @@ -614,11 +653,23 @@ export class DefaultConverter implements Converter { ): string { const templateUrl = template.parse(path); const params = {}; + const pathObj = spec.paths[path][method]; + // TODO: escape a reference token (i.g. path) + const jsonPointer = `/paths/${path}/${method}`; - if (typeof spec.paths[path][method].parameters !== 'undefined') { - for (const param of spec.paths[path][method].parameters) { + if (typeof pathObj.parameters !== 'undefined') { + for (const param of pathObj.parameters) { if (param?.in.toLowerCase() === 'path') { - const data = this.sample(param.schema || param, { spec }); + const data = this.sample( + param.schema || param, + {}, + { + spec, + jsonPointer: `${jsonPointer}/parameters/${pathObj.parameters.indexOf( + param + )}${param.schema ? '/schema' : ''}` + } + ); Object.assign(params, { [param.name]: data }); } } @@ -653,13 +704,18 @@ export class DefaultConverter implements Converter { private parseUrls(spec: OpenAPI.Document): string[] { if (this.isOASV3(spec) && spec.servers?.length) { - return spec.servers.map((server: OpenAPIV3.ServerObject) => { + return spec.servers.map((server: OpenAPIV3.ServerObject, idx: number) => { const variables = server.variables || {}; const templateUrl = template.parse(server.url); const params = {}; for (const [param, variable] of Object.entries(variables)) { - const data = this.sample(variable, { spec }); + const data = this.sample( + variable, + {}, + // TODO: escape a reference token (i.g. param) + { spec, jsonPointer: `/servers/${idx}/variables/${param}` } + ); Object.assign(params, { [param]: data }); } @@ -686,14 +742,16 @@ export class DefaultConverter implements Converter { private sample( schema: Schema, - options?: { + options?: Options, + context?: { spec?: OpenAPI.Document; + jsonPointer?: string; } & Options ): any | undefined { try { - return sample(schema, options, options?.spec); + return sample(schema, options, context?.spec); } catch (e) { - throw new ConvertError(e.message); + throw new ConvertError(e.message, context?.jsonPointer); } }