Skip to content

Commit dacc359

Browse files
authored
Merge pull request #855 from BitGo/DX-592
feat: consolidate comments from nested levels of schemas
2 parents 8ccc1a1 + 8b22044 commit dacc359

File tree

3 files changed

+476
-37
lines changed

3 files changed

+476
-37
lines changed

packages/openapi-generator/src/comments.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { parse as parseComment, Block } from 'comment-parser';
2+
import { Schema } from './ir';
23

34
export function leadingComment(
45
src: string,
@@ -42,3 +43,69 @@ export function leadingComment(
4243
const parsedComment = parseComment(commentString);
4344
return parsedComment;
4445
}
46+
47+
/**
48+
*
49+
* @param schema the schema to get all comments from
50+
* @returns an array of all comments in the schema
51+
*/
52+
export function getAllSchemaComments(schema: Schema): Block[] {
53+
const result = [];
54+
55+
/** Push the first comment */
56+
if (schema.comment) {
57+
result.push(schema.comment);
58+
}
59+
60+
/** Push the comments of the subschemas in CombinedTypes (union, intersection, etc) */
61+
if ('schemas' in schema) {
62+
// combined type
63+
for (const s of schema.schemas) {
64+
result.push(...getAllSchemaComments(s));
65+
}
66+
}
67+
68+
return result;
69+
}
70+
71+
/**
72+
*
73+
* @param schema the schema to combine comments from
74+
* @returns a combined comment from all comments in the schema
75+
*/
76+
export function combineComments(schema: Schema): Block | undefined {
77+
const comments = getAllSchemaComments(schema);
78+
79+
const tagSet = new Set<string>();
80+
81+
// Empty comment block where we will build the result
82+
const result: Block = {
83+
tags: [],
84+
description: '',
85+
problems: [],
86+
source: [],
87+
};
88+
89+
if (comments.length === 0) return undefined;
90+
91+
// Only use the first description if it exists, we don't wanna accidentally pull a description from a lower level schema
92+
if (comments[0]?.description && comments[0].description !== '') {
93+
result.description = comments[0].description;
94+
}
95+
96+
// Add all seen tags, problems, and source comments to the result
97+
for (const comment of comments) {
98+
for (const tag of comment.tags) {
99+
// Only add the tag if we haven't seen it before. Otherwise, the higher level tag is 'probably' the more relevant tag.
100+
if (!tagSet.has(tag.tag)) {
101+
result.tags.push(tag);
102+
tagSet.add(tag.tag);
103+
}
104+
}
105+
106+
result.problems.push(...comment.problems);
107+
result.source.push(...comment.source);
108+
}
109+
110+
return result;
111+
}

packages/openapi-generator/src/optimize.ts

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { combineComments } from './comments';
12
import { isPrimitive, type Primitive, type Schema } from './ir';
23

34
export type OptimizeFn = (schema: Schema) => Schema;
@@ -167,12 +168,21 @@ export function filterUndefinedUnion(schema: Schema): [boolean, Schema] {
167168
if (schemas.length === 0) {
168169
return [true, { type: 'undefined' }];
169170
} else if (schemas.length === 1) {
170-
return [true, schemas[0]!];
171+
return [true, withComment(schemas[0]!, schema)];
171172
} else {
172-
return [true, { type: 'union', schemas }];
173+
return [true, withComment({ type: 'union', schemas }, schema)];
173174
}
174175
}
175176

177+
// This function is a helper that adds back any comments that were removed during optimization
178+
function withComment(newSchema: Schema, oldSchema: Schema): Schema {
179+
if (oldSchema.comment) {
180+
newSchema.comment = combineComments(oldSchema);
181+
}
182+
183+
return newSchema;
184+
}
185+
176186
export function optimize(schema: Schema): Schema {
177187
if (schema.type === 'object') {
178188
const properties: Record<string, Schema> = {};
@@ -183,11 +193,6 @@ export function optimize(schema: Schema): Schema {
183193
continue;
184194
}
185195
const [isOptional, filteredSchema] = filterUndefinedUnion(optimized);
186-
187-
if (prop.comment) {
188-
filteredSchema.comment = prop.comment;
189-
}
190-
191196
properties[key] = filteredSchema;
192197

193198
if (schema.required.indexOf(key) >= 0 && !isOptional) {
@@ -197,48 +202,34 @@ export function optimize(schema: Schema): Schema {
197202

198203
const schemaObject: Schema = { type: 'object', properties, required };
199204

200-
// only add comment field if there is a comment
201-
if (schema.comment) {
202-
return { ...schemaObject, comment: schema.comment };
203-
}
204-
205-
return schemaObject;
205+
return withComment(schemaObject, schema);
206206
} else if (schema.type === 'intersection') {
207207
const newSchema = foldIntersection(schema, optimize);
208-
if (schema.comment) {
209-
return { ...newSchema, comment: schema.comment };
210-
}
211-
return newSchema;
208+
209+
return withComment(newSchema, schema);
212210
} else if (schema.type === 'union') {
213211
const consolidated = consolidateUnion(schema);
214212
const simplified = simplifyUnion(consolidated, optimize);
215213
const merged = mergeUnions(simplified);
216214

217-
if (schema.comment) {
218-
return { ...merged, comment: schema.comment };
219-
}
220-
221-
return merged;
215+
return withComment(merged, schema);
222216
} else if (schema.type === 'array') {
223217
const optimized = optimize(schema.items);
224-
if (schema.comment) {
225-
return { type: 'array', items: optimized, comment: schema.comment };
226-
}
227-
return { type: 'array', items: optimized };
218+
219+
return withComment({ type: 'array', items: optimized }, schema);
228220
} else if (schema.type === 'record') {
229-
return {
230-
type: 'record',
231-
...(schema.domain ? { domain: optimize(schema.domain) } : {}),
232-
codomain: optimize(schema.codomain),
233-
...(schema.comment ? { comment: schema.comment } : {}),
234-
};
221+
return withComment(
222+
{
223+
type: 'record',
224+
...(schema.domain ? { domain: optimize(schema.domain) } : {}),
225+
codomain: optimize(schema.codomain),
226+
},
227+
schema,
228+
);
235229
} else if (schema.type === 'tuple') {
236230
const schemas = schema.schemas.map(optimize);
237-
return { type: 'tuple', schemas };
231+
return withComment({ type: 'tuple', schemas }, schema);
238232
} else if (schema.type === 'ref') {
239-
if (schema.comment) {
240-
return { ...schema, comment: schema.comment };
241-
}
242233
return schema;
243234
} else {
244235
return schema;

0 commit comments

Comments
 (0)