Skip to content

Commit 514872b

Browse files
committed
Implementation of multifile swagger generation
1 parent 809771b commit 514872b

File tree

22 files changed

+1697
-227
lines changed

22 files changed

+1697
-227
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
changeKind: fix
3+
packages:
4+
- "@azure-tools/typespec-autorest-canonical"
5+
---
6+
7+
Adapt to shared type changes in typespec-autorest
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
changeKind: feature
3+
packages:
4+
- "@azure-tools/typespec-autorest"
5+
- "@azure-tools/typespec-azure-resource-manager"
6+
---
7+
8+
Add support for multiple output files in typespec-autorest

packages/typespec-autorest-canonical/src/emitter.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,10 @@ async function emitAllServices(
9595
service,
9696
version: canonicalVersion,
9797
tcgcSdkContext,
98+
multiService: services.length > 1,
9899
};
99-
const result = await getOpenAPIForService(context, options);
100+
const results = await getOpenAPIForService(context, options);
101+
const result = results[0];
100102
const includedVersions = getVersion(program, service.type)
101103
?.getVersions()
102104
?.map((item) => item.value ?? item.name);

packages/typespec-autorest/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,12 @@ Determine whether and how to emit schemas for common-types rather than referenci
163163

164164
Strategy for applying XML serialization metadata to schemas.
165165

166+
### `output-splitting`
167+
168+
**Type:** `"legacy-feature-files"`
169+
170+
Determines whether output should be split into multiple files. The only supported option for splitting is "legacy-feature-files", which uses the typespec-azure-resource-manager `@feature` decorators to split into output files based on feature.
171+
166172
## Decorators
167173

168174
### Autorest

packages/typespec-autorest/src/emit.ts

Lines changed: 109 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { getVersioningMutators } from "@typespec/versioning";
2323
import { AutorestEmitterOptions, getTracer, reportDiagnostic } from "./lib.js";
2424
import {
2525
AutorestDocumentEmitterOptions,
26+
createDocumentProxy,
2627
getOpenAPIForService,
2728
sortOpenAPIDocument,
2829
} from "./openapi.js";
@@ -113,16 +114,34 @@ export function resolveAutorestOptions(
113114
emitLroOptions: resolvedOptions["emit-lro-options"],
114115
emitCommonTypesSchema: resolvedOptions["emit-common-types-schema"],
115116
xmlStrategy: resolvedOptions["xml-strategy"],
117+
outputSplitting: resolvedOptions["output-splitting"],
116118
};
117119
}
118120

119-
export async function getAllServicesAtAllVersions(
121+
function getEmitterContext(
120122
program: Program,
123+
service: Service,
121124
options: ResolvedAutorestEmitterOptions,
122-
): Promise<AutorestServiceRecord[]> {
125+
multiService: boolean = false,
126+
version?: string,
127+
): AutorestEmitterContext {
123128
const tcgcSdkContext = createTCGCContext(program, "@azure-tools/typespec-autorest");
124129
tcgcSdkContext.enableLegacyHierarchyBuilding = true;
130+
return {
131+
program,
132+
outputFile: resolveOutputFile(program, service, multiService, options, version),
133+
service: service,
134+
tcgcSdkContext,
135+
proxy: createDocumentProxy(program, service, options, version),
136+
version: version,
137+
multiService: multiService,
138+
};
139+
}
125140

141+
export async function getAllServicesAtAllVersions(
142+
program: Program,
143+
options: ResolvedAutorestEmitterOptions,
144+
): Promise<AutorestServiceRecord[]> {
126145
const services = listServices(program);
127146
if (services.length === 0) {
128147
services.push({ type: program.getGlobalNamespaceType() });
@@ -133,33 +152,60 @@ export async function getAllServicesAtAllVersions(
133152
const versions = getVersioningMutators(program, service.type);
134153

135154
if (versions === undefined) {
136-
const context: AutorestEmitterContext = {
155+
const context: AutorestEmitterContext = getEmitterContext(
137156
program,
138-
outputFile: resolveOutputFile(program, service, services.length > 1, options),
139-
service: service,
140-
tcgcSdkContext,
141-
};
142-
143-
const result = await getOpenAPIForService(context, options);
144-
serviceRecords.push({
145157
service,
146-
versioned: false,
147-
...result,
148-
});
158+
options,
159+
services.length > 1,
160+
);
161+
162+
const results = await getOpenAPIForService(context, options);
163+
for (const result of results) {
164+
const newResult = { ...result };
165+
newResult.outputFile = resolveOutputFile(
166+
program,
167+
service,
168+
services.length > 1,
169+
options,
170+
undefined,
171+
result.feature,
172+
);
173+
serviceRecords.push({
174+
service,
175+
versioned: false,
176+
...newResult,
177+
});
178+
}
149179
} else if (versions.kind === "transient") {
150-
const context: AutorestEmitterContext = {
180+
const context: AutorestEmitterContext = getEmitterContext(
151181
program,
152-
outputFile: resolveOutputFile(program, service, services.length > 1, options),
153-
service: service,
154-
tcgcSdkContext,
155-
};
156-
157-
const result = await getVersionSnapshotDocument(context, versions.mutator, options);
158-
serviceRecords.push({
159182
service,
160-
versioned: false,
161-
...result,
162-
});
183+
options,
184+
services.length > 1,
185+
);
186+
187+
const results = await getVersionSnapshotDocument(
188+
context,
189+
versions.mutator,
190+
options,
191+
services.length > 1,
192+
);
193+
for (const result of results) {
194+
const newResult = { ...result };
195+
newResult.outputFile = resolveOutputFile(
196+
program,
197+
service,
198+
services.length > 1,
199+
options,
200+
undefined,
201+
result.feature,
202+
);
203+
serviceRecords.push({
204+
service,
205+
versioned: false,
206+
...newResult,
207+
});
208+
}
163209
} else {
164210
const filteredVersions = versions.snapshots.filter(
165211
(v) => !options.version || options.version === v.version?.value,
@@ -176,26 +222,36 @@ export async function getAllServicesAtAllVersions(
176222
serviceRecords.push(serviceRecord);
177223

178224
for (const record of filteredVersions) {
179-
const context: AutorestEmitterContext = {
225+
const context: AutorestEmitterContext = getEmitterContext(
180226
program,
181-
outputFile: resolveOutputFile(
227+
service,
228+
options,
229+
services.length > 1,
230+
record.version?.value,
231+
);
232+
233+
const results = await getVersionSnapshotDocument(
234+
context,
235+
record.mutator,
236+
options,
237+
services.length > 1,
238+
);
239+
for (const result of results) {
240+
const newResult = { ...result };
241+
newResult.outputFile = resolveOutputFile(
182242
program,
183243
service,
184244
services.length > 1,
185245
options,
186-
record.version?.value,
187-
),
188-
service,
189-
version: record.version?.value,
190-
tcgcSdkContext,
191-
};
192-
193-
const result = await getVersionSnapshotDocument(context, record.mutator, options);
194-
serviceRecord.versions.push({
195-
...result,
196-
service,
197-
version: record.version!.value,
198-
});
246+
record.version!.value,
247+
result.feature,
248+
);
249+
serviceRecord.versions.push({
250+
...newResult,
251+
service,
252+
version: record.version!.value,
253+
});
254+
}
199255
}
200256
}
201257
}
@@ -207,6 +263,7 @@ async function getVersionSnapshotDocument(
207263
context: AutorestEmitterContext,
208264
mutator: unsafe_MutatorWithNamespace,
209265
options: ResolvedAutorestEmitterOptions,
266+
multiService: boolean = false,
210267
) {
211268
const subgraph = unsafe_mutateSubgraphWithNamespace(
212269
context.program,
@@ -215,10 +272,15 @@ async function getVersionSnapshotDocument(
215272
);
216273

217274
compilerAssert(subgraph.type.kind === "Namespace", "Should not have mutated to another type");
218-
const document = await getOpenAPIForService(
219-
{ ...context, service: getService(context.program, subgraph.type)! },
275+
const service = getService(context.program, subgraph.type)!;
276+
const newContext: AutorestEmitterContext = getEmitterContext(
277+
context.program,
278+
service,
220279
options,
280+
multiService,
281+
context.version,
221282
);
283+
const document = await getOpenAPIForService(newContext, options);
222284

223285
return document;
224286
}
@@ -247,6 +309,9 @@ async function emitOutput(
247309
result: AutorestEmitterResult,
248310
options: ResolvedAutorestEmitterOptions,
249311
) {
312+
const currentFeature = result.feature;
313+
if (currentFeature !== undefined && result.context.proxy !== undefined)
314+
result.context.proxy.setCurrentFeature(currentFeature);
250315
const sortedDocument = sortOpenAPIDocument(result.document);
251316

252317
// Write out the OpenAPI document to the output path
@@ -277,12 +342,13 @@ function prettierOutput(output: string) {
277342
return output + "\n";
278343
}
279344

280-
function resolveOutputFile(
345+
export function resolveOutputFile(
281346
program: Program,
282347
service: Service,
283348
multipleServices: boolean,
284349
options: ResolvedAutorestEmitterOptions,
285350
version?: string,
351+
feature?: string,
286352
): string {
287353
const azureResourceProviderFolder = options.azureResourceProviderFolder;
288354
if (azureResourceProviderFolder) {
@@ -301,6 +367,7 @@ function resolveOutputFile(
301367
: "stable"
302368
: undefined,
303369
version,
370+
feature,
304371
});
305372

306373
return resolvePath(options.outputDir, interpolated);

packages/typespec-autorest/src/lib.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ export interface AutorestEmitterOptions {
113113
* @default "xml-service"
114114
*/
115115
"xml-strategy"?: "xml-service" | "none";
116+
117+
/**
118+
* Determines whether output should be split into multiple files. The only supported option for splitting is "legacy-feature-files",
119+
* which uses the typespec-azure-resource-manager `@feature` decorators to split into output files based on feature.
120+
*/
121+
"output-splitting"?: "legacy-feature-files";
116122
}
117123

118124
const EmitterOptionsSchema: JSONSchemaType<AutorestEmitterOptions> = {
@@ -238,6 +244,13 @@ const EmitterOptionsSchema: JSONSchemaType<AutorestEmitterOptions> = {
238244
default: "xml-service",
239245
description: "Strategy for applying XML serialization metadata to schemas.",
240246
},
247+
"output-splitting": {
248+
type: "string",
249+
enum: ["legacy-feature-files"],
250+
nullable: true,
251+
description:
252+
'Determines whether output should be split into multiple files. The only supported option for splitting is "legacy-feature-files", which uses the typespec-azure-resource-manager `@feature` decorators to split into output files based on feature.',
253+
},
241254
},
242255
required: [],
243256
};

0 commit comments

Comments
 (0)