@typespec/openapi3
Advanced tools
Comparing version 0.54.0-dev.4 to 0.54.0-dev.5
@@ -224,3 +224,3 @@ import { JSONSchemaType } from "@typespec/compiler"; | ||
}, OpenAPI3EmitterOptions, never>; | ||
export declare const reportDiagnostic: <C extends "oneof-union" | "inconsistent-shared-route-request-visibility" | "invalid-server-variable" | "invalid-format" | "resource-namespace" | "path-query" | "duplicate-body" | "duplicate-header" | "status-code-in-default-response" | "invalid-schema" | "union-null" | "empty-union" | "empty-enum" | "enum-unique-type" | "invalid-default" | "inline-cycle" | "unsupported-status-code-range" | "unsupported-auth", M extends keyof { | ||
export declare const createDiagnostic: <C extends "oneof-union" | "inconsistent-shared-route-request-visibility" | "invalid-server-variable" | "invalid-format" | "resource-namespace" | "path-query" | "duplicate-body" | "duplicate-header" | "status-code-in-default-response" | "invalid-schema" | "union-null" | "empty-union" | "empty-enum" | "enum-unique-type" | "invalid-default" | "inline-cycle" | "unsupported-status-code-range" | "unsupported-auth", M extends keyof { | ||
"oneof-union": { | ||
@@ -280,2 +280,112 @@ readonly default: "@oneOf decorator can only be used on a union or a model property which type is a union."; | ||
}; | ||
}[C]>(diag: import("@typespec/compiler").DiagnosticReport<{ | ||
"oneof-union": { | ||
readonly default: "@oneOf decorator can only be used on a union or a model property which type is a union."; | ||
}; | ||
"inconsistent-shared-route-request-visibility": { | ||
readonly default: "All operations with `@sharedRoutes` must have the same `@requestVisibility`."; | ||
}; | ||
"invalid-server-variable": { | ||
readonly default: import("@typespec/compiler").CallableMessage<["propName"]>; | ||
}; | ||
"invalid-format": { | ||
readonly default: import("@typespec/compiler").CallableMessage<["value", "paramType"]>; | ||
}; | ||
"resource-namespace": { | ||
readonly default: "Resource goes on namespace"; | ||
}; | ||
"path-query": { | ||
readonly default: "OpenAPI does not allow paths containing a query string."; | ||
}; | ||
"duplicate-body": { | ||
readonly default: "Duplicate @body declarations on response type"; | ||
}; | ||
"duplicate-header": { | ||
readonly default: import("@typespec/compiler").CallableMessage<["header"]>; | ||
}; | ||
"status-code-in-default-response": { | ||
readonly default: "a default response should not have an explicit status code"; | ||
}; | ||
"invalid-schema": { | ||
readonly default: import("@typespec/compiler").CallableMessage<["type"]>; | ||
}; | ||
"union-null": { | ||
readonly default: "Cannot have a union containing only null types."; | ||
}; | ||
"empty-union": { | ||
readonly default: "Empty unions are not supported for OpenAPI v3 - enums must have at least one value."; | ||
}; | ||
"empty-enum": { | ||
readonly default: "Empty enums are not supported for OpenAPI v3 - enums must have at least one value."; | ||
}; | ||
"enum-unique-type": { | ||
readonly default: "Enums are not supported unless all options are literals of the same type."; | ||
}; | ||
"invalid-default": { | ||
readonly default: import("@typespec/compiler").CallableMessage<["type"]>; | ||
}; | ||
"inline-cycle": { | ||
readonly default: import("@typespec/compiler").CallableMessage<["type"]>; | ||
}; | ||
"unsupported-status-code-range": { | ||
readonly default: import("@typespec/compiler").CallableMessage<["start", "end"]>; | ||
}; | ||
"unsupported-auth": { | ||
readonly default: import("@typespec/compiler").CallableMessage<["authType"]>; | ||
}; | ||
}, C, M>) => import("@typespec/compiler").Diagnostic, reportDiagnostic: <C extends "oneof-union" | "inconsistent-shared-route-request-visibility" | "invalid-server-variable" | "invalid-format" | "resource-namespace" | "path-query" | "duplicate-body" | "duplicate-header" | "status-code-in-default-response" | "invalid-schema" | "union-null" | "empty-union" | "empty-enum" | "enum-unique-type" | "invalid-default" | "inline-cycle" | "unsupported-status-code-range" | "unsupported-auth", M extends keyof { | ||
"oneof-union": { | ||
readonly default: "@oneOf decorator can only be used on a union or a model property which type is a union."; | ||
}; | ||
"inconsistent-shared-route-request-visibility": { | ||
readonly default: "All operations with `@sharedRoutes` must have the same `@requestVisibility`."; | ||
}; | ||
"invalid-server-variable": { | ||
readonly default: import("@typespec/compiler").CallableMessage<["propName"]>; | ||
}; | ||
"invalid-format": { | ||
readonly default: import("@typespec/compiler").CallableMessage<["value", "paramType"]>; | ||
}; | ||
"resource-namespace": { | ||
readonly default: "Resource goes on namespace"; | ||
}; | ||
"path-query": { | ||
readonly default: "OpenAPI does not allow paths containing a query string."; | ||
}; | ||
"duplicate-body": { | ||
readonly default: "Duplicate @body declarations on response type"; | ||
}; | ||
"duplicate-header": { | ||
readonly default: import("@typespec/compiler").CallableMessage<["header"]>; | ||
}; | ||
"status-code-in-default-response": { | ||
readonly default: "a default response should not have an explicit status code"; | ||
}; | ||
"invalid-schema": { | ||
readonly default: import("@typespec/compiler").CallableMessage<["type"]>; | ||
}; | ||
"union-null": { | ||
readonly default: "Cannot have a union containing only null types."; | ||
}; | ||
"empty-union": { | ||
readonly default: "Empty unions are not supported for OpenAPI v3 - enums must have at least one value."; | ||
}; | ||
"empty-enum": { | ||
readonly default: "Empty enums are not supported for OpenAPI v3 - enums must have at least one value."; | ||
}; | ||
"enum-unique-type": { | ||
readonly default: "Enums are not supported unless all options are literals of the same type."; | ||
}; | ||
"invalid-default": { | ||
readonly default: import("@typespec/compiler").CallableMessage<["type"]>; | ||
}; | ||
"inline-cycle": { | ||
readonly default: import("@typespec/compiler").CallableMessage<["type"]>; | ||
}; | ||
"unsupported-status-code-range": { | ||
readonly default: import("@typespec/compiler").CallableMessage<["start", "end"]>; | ||
}; | ||
"unsupported-auth": { | ||
readonly default: import("@typespec/compiler").CallableMessage<["authType"]>; | ||
}; | ||
}[C]>(program: import("@typespec/compiler").Program, diag: import("@typespec/compiler").DiagnosticReport<{ | ||
@@ -282,0 +392,0 @@ "oneof-union": { |
@@ -180,3 +180,3 @@ import { createTypeSpecLibrary, paramMessage } from "@typespec/compiler"; | ||
export const $lib = createTypeSpecLibrary(libDef); | ||
export const { reportDiagnostic, createStateSymbol } = $lib; | ||
export const { createDiagnostic, reportDiagnostic, createStateSymbol } = $lib; | ||
//# sourceMappingURL=lib.js.map |
@@ -1,4 +0,15 @@ | ||
import { EmitContext, NewLine } from "@typespec/compiler"; | ||
import { EmitContext, NewLine, Program } from "@typespec/compiler"; | ||
import { FileType, OpenAPI3EmitterOptions } from "./lib.js"; | ||
import { OpenAPI3ServiceRecord } from "./types.js"; | ||
export declare function $onEmit(context: EmitContext<OpenAPI3EmitterOptions>): Promise<void>; | ||
type IrrelevantOpenAPI3EmitterOptionsForObject = "file-type" | "output-file" | "new-line"; | ||
/** | ||
* Get the OpenAPI 3 document records from the given program. The documents are | ||
* returned as a JS object. | ||
* | ||
* @param program The program to emit to OpenAPI 3 | ||
* @param options OpenAPI 3 emit options | ||
* @returns An array of OpenAPI 3 document records. | ||
*/ | ||
export declare function getOpenAPI3(program: Program, options?: Omit<OpenAPI3EmitterOptions, IrrelevantOpenAPI3EmitterOptionsForObject>): Promise<OpenAPI3ServiceRecord[]>; | ||
export declare function resolveOptions(context: EmitContext<OpenAPI3EmitterOptions>): ResolvedOpenAPI3EmitterOptions; | ||
@@ -12,2 +23,3 @@ export interface ResolvedOpenAPI3EmitterOptions { | ||
} | ||
export {}; | ||
//# sourceMappingURL=openapi.d.ts.map |
@@ -1,2 +0,3 @@ | ||
import { compilerAssert, emitFile, getAllTags, getAnyExtensionFromPath, getDoc, getEncode, getFormat, getKnownValues, getMaxItems, getMaxLength, getMaxValue, getMaxValueExclusive, getMinItems, getMinLength, getMinValue, getMinValueExclusive, getNamespaceFullName, getPattern, getService, getSummary, ignoreDiagnostics, interpolatePath, isDeprecated, isGlobalNamespace, isNeverType, isSecret, isVoidType, listServices, navigateTypesInNamespace, projectProgram, resolvePath, } from "@typespec/compiler"; | ||
import { compilerAssert, createDiagnosticCollector, emitFile, getAllTags, getAnyExtensionFromPath, getDoc, getEncode, getFormat, getKnownValues, getMaxItems, getMaxLength, getMaxValue, getMaxValueExclusive, getMinItems, getMinLength, getMinValue, getMinValueExclusive, getNamespaceFullName, getPattern, getService, getSummary, ignoreDiagnostics, interpolatePath, isDeprecated, isGlobalNamespace, isNeverType, isSecret, isVoidType, listServices, navigateTypesInNamespace, projectProgram, resolvePath, } from "@typespec/compiler"; | ||
import { createAssetEmitter } from "@typespec/compiler/emitter-framework"; | ||
import { createMetadataInfo, getAuthentication, getHttpService, getServers, getStatusCodeDescription, isContentTypeHeader, isOverloadSameEndpoint, reportIfNoRoutes, resolveRequestVisibility, Visibility, } from "@typespec/http"; | ||
@@ -7,3 +8,3 @@ import { getExtensions, getExternalDocs, getInfo, getOpenAPITypeName, getParameterKey, isDefaultResponse, isReadonlyProperty, resolveOperationId, shouldInline, } from "@typespec/openapi"; | ||
import { getRef } from "./decorators.js"; | ||
import { reportDiagnostic } from "./lib.js"; | ||
import { createDiagnostic } from "./lib.js"; | ||
import { getDefaultValue, OpenAPI3SchemaEmitter } from "./schema-emitter.js"; | ||
@@ -23,2 +24,24 @@ import { deepEquals } from "./util.js"; | ||
} | ||
/** | ||
* Get the OpenAPI 3 document records from the given program. The documents are | ||
* returned as a JS object. | ||
* | ||
* @param program The program to emit to OpenAPI 3 | ||
* @param options OpenAPI 3 emit options | ||
* @returns An array of OpenAPI 3 document records. | ||
*/ | ||
export async function getOpenAPI3(program, options = {}) { | ||
const context = { | ||
program, | ||
// this value doesn't matter for getting the OpenAPI3 objects | ||
emitterOutputDir: "tsp-output", | ||
options: options, | ||
getAssetEmitter(TypeEmitterClass) { | ||
return createAssetEmitter(program, TypeEmitterClass, this); | ||
}, | ||
}; | ||
const resolvedOptions = resolveOptions(context); | ||
const emitter = createOAPIEmitter(context, resolvedOptions); | ||
return emitter.getOpenAPI(); | ||
} | ||
function findFileTypeFromFilename(filename) { | ||
@@ -55,2 +78,3 @@ if (filename === undefined) { | ||
let root; | ||
let diagnostics; | ||
let currentService; | ||
@@ -79,3 +103,39 @@ // Get the service namespace string for use in name shortening | ||
}; | ||
return { emitOpenAPI }; | ||
return { emitOpenAPI, getOpenAPI }; | ||
async function emitOpenAPI() { | ||
const services = await getOpenAPI(); | ||
// first, emit diagnostics | ||
for (const serviceRecord of services) { | ||
if (serviceRecord.versioned) { | ||
for (const documentRecord of serviceRecord.versions) { | ||
program.reportDiagnostics(documentRecord.diagnostics); | ||
} | ||
} | ||
else { | ||
program.reportDiagnostics(serviceRecord.diagnostics); | ||
} | ||
} | ||
if (program.compilerOptions.noEmit || program.hasError()) { | ||
return; | ||
} | ||
const multipleService = services.length > 1; | ||
for (const serviceRecord of services) { | ||
if (serviceRecord.versioned) { | ||
for (const documentRecord of serviceRecord.versions) { | ||
await emitFile(program, { | ||
path: resolveOutputFile(serviceRecord.service, multipleService, documentRecord.version), | ||
content: serializeDocument(documentRecord.document, options.fileType), | ||
newLine: options.newLine, | ||
}); | ||
} | ||
} | ||
else { | ||
await emitFile(program, { | ||
path: resolveOutputFile(serviceRecord.service, multipleService), | ||
content: serializeDocument(serviceRecord.document, options.fileType), | ||
newLine: options.newLine, | ||
}); | ||
} | ||
} | ||
} | ||
function initializeEmitter(service, version) { | ||
@@ -116,2 +176,3 @@ var _a, _b, _c; | ||
}; | ||
diagnostics = createDiagnosticCollector(); | ||
const servers = getServers(program, service.type); | ||
@@ -148,7 +209,7 @@ if (servers) { | ||
if (!isValid) { | ||
reportDiagnostic(program, { | ||
diagnostics.add(createDiagnostic({ | ||
code: "invalid-server-variable", | ||
format: { propName: prop.name }, | ||
target: prop, | ||
}); | ||
})); | ||
} | ||
@@ -189,3 +250,4 @@ return isValid; | ||
} | ||
async function emitOpenAPI() { | ||
async function getOpenAPI() { | ||
const serviceRecords = []; | ||
const services = listServices(program); | ||
@@ -196,2 +258,42 @@ if (services.length === 0) { | ||
for (const service of services) { | ||
const versions = buildVersionProjections(program, service.type); | ||
if (versions.length === 1 && versions[0].version === undefined) { | ||
// non-versioned spec | ||
const document = await getProjectedOpenAPIDocument(service, versions[0]); | ||
if (document === undefined) { | ||
// an error occurred producing this document, so don't return it | ||
return serviceRecords; | ||
} | ||
serviceRecords.push({ | ||
service, | ||
versioned: false, | ||
document: document[0], | ||
diagnostics: document[1], | ||
}); | ||
} | ||
else { | ||
// versioned spec | ||
const serviceRecord = { | ||
service, | ||
versioned: true, | ||
versions: [], | ||
}; | ||
serviceRecords.push(serviceRecord); | ||
for (const record of versions) { | ||
const document = await getProjectedOpenAPIDocument(service, record); | ||
if (document === undefined) { | ||
// an error occurred producing this document | ||
continue; | ||
} | ||
serviceRecord.versions.push({ | ||
service, | ||
version: record.version, | ||
document: document[0], | ||
diagnostics: document[1], | ||
}); | ||
} | ||
} | ||
} | ||
return serviceRecords; | ||
async function getProjectedOpenAPIDocument(service, record) { | ||
const commonProjections = [ | ||
@@ -204,13 +306,11 @@ { | ||
const originalProgram = program; | ||
const versions = buildVersionProjections(program, service.type); | ||
for (const record of versions) { | ||
const projectedProgram = (program = projectProgram(originalProgram, [ | ||
...commonProjections, | ||
...record.projections, | ||
])); | ||
const projectedServiceNs = projectedProgram.projector.projectedTypes.get(service.type); | ||
await emitOpenAPIFromVersion(projectedServiceNs === projectedProgram.getGlobalNamespaceType() | ||
? { type: projectedProgram.getGlobalNamespaceType() } | ||
: getService(program, projectedServiceNs), services.length > 1, record.version); | ||
} | ||
const projectedProgram = (program = projectProgram(originalProgram, [ | ||
...commonProjections, | ||
...record.projections, | ||
])); | ||
const projectedServiceNs = projectedProgram.projector.projectedTypes.get(service.type); | ||
const document = await getOpenApiFromVersion(projectedServiceNs === projectedProgram.getGlobalNamespaceType() | ||
? { type: projectedProgram.getGlobalNamespaceType() } | ||
: getService(program, projectedServiceNs), record.version); | ||
return document; | ||
} | ||
@@ -309,7 +409,7 @@ } | ||
function rangeToOpenAPI(range, diagnosticTarget) { | ||
const reportInvalid = () => reportDiagnostic(program, { | ||
const reportInvalid = () => diagnostics.add(createDiagnostic({ | ||
code: "unsupported-status-code-range", | ||
format: { start: String(range.start), end: String(range.end) }, | ||
target: diagnosticTarget, | ||
}); | ||
})); | ||
const codes = []; | ||
@@ -426,3 +526,3 @@ let start = range.start; | ||
} | ||
async function emitOpenAPIFromVersion(service, multipleService, version) { | ||
async function getOpenApiFromVersion(service, version) { | ||
initializeEmitter(service, version); | ||
@@ -451,10 +551,3 @@ try { | ||
} | ||
if (!program.compilerOptions.noEmit && !program.hasError()) { | ||
// Write out the OpenAPI document to the output path | ||
await emitFile(program, { | ||
path: resolveOutputFile(service, multipleService, version), | ||
content: serializeDocument(root, options.fileType), | ||
newLine: options.newLine, | ||
}); | ||
} | ||
return [root, diagnostics.diagnostics]; | ||
} | ||
@@ -525,6 +618,6 @@ catch (err) { | ||
if (visibilities.some((v) => v !== visibilities[0])) { | ||
reportDiagnostic(program, { | ||
diagnostics.add(createDiagnostic({ | ||
code: "inconsistent-shared-route-request-visibility", | ||
target: ops[0], | ||
}); | ||
})); | ||
} | ||
@@ -553,3 +646,3 @@ const visibility = resolveRequestVisibility(program, shared.operations[0], verb); | ||
if (fullPath.indexOf("?") > 0) { | ||
reportDiagnostic(program, { code: "path-query", target: op }); | ||
diagnostics.add(createDiagnostic({ code: "path-query", target: op })); | ||
return; | ||
@@ -653,7 +746,7 @@ } | ||
if (!deepEquals(existing, headerVal)) { | ||
reportDiagnostic(program, { | ||
diagnostics.add(createDiagnostic({ | ||
code: "duplicate-header", | ||
format: { header: key }, | ||
target: target, | ||
}); | ||
})); | ||
} | ||
@@ -717,7 +810,7 @@ continue; | ||
case "circular": | ||
reportDiagnostic(program, { | ||
diagnostics.add(createDiagnostic({ | ||
code: "inline-cycle", | ||
format: { type: getOpenAPITypeName(program, type, typeNameOptions) }, | ||
target: type, | ||
}); | ||
})); | ||
return {}; | ||
@@ -735,7 +828,7 @@ case "none": | ||
case "circular": | ||
reportDiagnostic(program, { | ||
diagnostics.add(createDiagnostic({ | ||
code: "inline-cycle", | ||
format: { type: getOpenAPITypeName(program, type, typeNameOptions) }, | ||
target: type, | ||
}); | ||
})); | ||
return {}; | ||
@@ -949,3 +1042,3 @@ case "none": | ||
default: | ||
reportDiagnostic(program, { | ||
diagnostics.add(createDiagnostic({ | ||
code: "invalid-format", | ||
@@ -957,3 +1050,3 @@ format: { | ||
target: parameter.param, | ||
}); | ||
})); | ||
return undefined; | ||
@@ -977,3 +1070,3 @@ } | ||
default: | ||
reportDiagnostic(program, { | ||
diagnostics.add(createDiagnostic({ | ||
code: "invalid-format", | ||
@@ -985,3 +1078,3 @@ format: { | ||
target: parameter.param, | ||
}); | ||
})); | ||
return undefined; | ||
@@ -1213,7 +1306,7 @@ } | ||
default: | ||
reportDiagnostic(program, { | ||
diagnostics.add(createDiagnostic({ | ||
code: "unsupported-auth", | ||
format: { authType: auth.type }, | ||
target: currentService.type, | ||
}); | ||
})); | ||
return undefined; | ||
@@ -1220,0 +1313,0 @@ } |
@@ -0,1 +1,2 @@ | ||
import { Diagnostic, Service } from "@typespec/compiler"; | ||
import { ExtensionKey } from "@typespec/openapi"; | ||
@@ -32,2 +33,38 @@ export type Extensions = { | ||
} | ||
/** | ||
* A record containing the the OpenAPI 3 documents corresponding to | ||
* a particular service definition. | ||
*/ | ||
export type OpenAPI3ServiceRecord = OpenAPI3UnversionedServiceRecord | OpenAPI3VersionedServiceRecord; | ||
export interface OpenAPI3UnversionedServiceRecord { | ||
/** The service that generated this OpenAPI document */ | ||
readonly service: Service; | ||
/** Whether the service is versioned */ | ||
readonly versioned: false; | ||
/** The OpenAPI 3 document */ | ||
readonly document: OpenAPI3Document; | ||
/** The diagnostics created for this document */ | ||
readonly diagnostics: readonly Diagnostic[]; | ||
} | ||
export interface OpenAPI3VersionedServiceRecord { | ||
/** The service that generated this OpenAPI document */ | ||
readonly service: Service; | ||
/** Whether the service is versioned */ | ||
readonly versioned: true; | ||
/** The OpenAPI 3 document records for each version of this service */ | ||
readonly versions: OpenAPI3VersionedDocumentRecord[]; | ||
} | ||
/** | ||
* A record containing an unversioned OpenAPI document and associated metadata. | ||
*/ | ||
export interface OpenAPI3VersionedDocumentRecord { | ||
/** The OpenAPI document*/ | ||
readonly document: OpenAPI3Document; | ||
/** The service that generated this OpenAPI document. */ | ||
readonly service: Service; | ||
/** The version of the service. Absent if the service is unversioned. */ | ||
readonly version: string; | ||
/** The diagnostics created for this version. */ | ||
readonly diagnostics: readonly Diagnostic[]; | ||
} | ||
export interface OpenAPI3Info extends Extensions { | ||
@@ -34,0 +71,0 @@ title: string; |
{ | ||
"name": "@typespec/openapi3", | ||
"version": "0.54.0-dev.4", | ||
"version": "0.54.0-dev.5", | ||
"author": "Microsoft Corporation", | ||
@@ -5,0 +5,0 @@ "description": "TypeSpec library for emitting OpenAPI 3.0 from the TypeSpec REST protocol binding", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
274537
3588