@@ -15,13 +15,25 @@ export interface SimpleRestAuthMapping {
1515 schemaV4 : string ;
1616}
1717
18+ export interface GenerageSchemaFromOpenApiSpecOptions {
19+ openApiSpec : OpenAPIV3 . Document ;
20+ namespace : string ;
21+ mappingType : MappingType ;
22+ serverBasePath ?: string ;
23+ }
24+
1825export class Tools {
1926 private static openAPIToCedarPrimitiveTypeMap = {
2027 string : { type : 'String' as const } ,
2128 number : { type : 'Long' as const } ,
2229 integer : { type : 'Long' as const } ,
2330 boolean : { type : 'Boolean' as const } ,
2431 }
32+ private static sanitizePath ( pathStr : string ) : string {
33+ const trimmed = pathStr . split ( '/' )
34+ . filter ( segment => segment !== '' ) ;
35+ return `/${ trimmed . join ( '/' ) } ` ;
36+ }
2537 /**
2638 * How action names are computed:
2739 * - If your API spec has operation id's then those are used as cedar actions
@@ -32,14 +44,42 @@ export class Tools {
3244 * @param namespace cedar namespace for your application
3345 * @returns
3446 */
35- public static generateApiMappingSchemaFromOpenAPISpec ( openApiSpec : OpenAPIV3 . Document , namespace : string , mappingType : MappingType ) : AuthMapping {
47+ public static generateApiMappingSchemaFromOpenAPISpec ( options : GenerageSchemaFromOpenApiSpecOptions ) : AuthMapping {
48+ const { openApiSpec, namespace, mappingType} = options ;
3649 if ( ! openApiSpec . paths ) {
3750 throw new Error ( 'Invalid OpenAPI spec - missing paths object' ) ;
3851 }
3952
4053 if ( ! namespace ) {
4154 throw new Error ( 'Invalid input - missing namespace' ) ;
4255 }
56+ const servers = openApiSpec . servers ;
57+
58+ if ( options . serverBasePath && Array . isArray ( servers ) ) {
59+ const basePathExistsInServersArray = servers
60+ . map ( server => server . url || '' )
61+ . some ( serverUrl => {
62+ const normalizedBasePath = this . sanitizePath ( options . serverBasePath || '' ) ;
63+ return serverUrl . endsWith ( normalizedBasePath ) || serverUrl . endsWith ( `${ normalizedBasePath } /` )
64+ } ) ;
65+ if ( ! basePathExistsInServersArray ) {
66+ throw new Error ( 'Base Path option was provided but it does not match any of the `servers` entries in the API spec.' ) ;
67+ }
68+ }
69+
70+ let basePath = '' ;
71+ if ( Array . isArray ( servers ) ) {
72+ if ( servers . length > 1 ) {
73+ if ( ! options . serverBasePath ) {
74+ throw new Error ( 'Invalid input. API spec specifies more than one `server` entry. Server Base Path parameter required for disambiguation.' ) ;
75+ }
76+ basePath = this . sanitizePath ( options . serverBasePath ) ;
77+ } else if ( servers . length === 1 ) {
78+ const fullBaseUrl = new URL ( servers [ 0 ] . url ) ;
79+ basePath = this . sanitizePath ( fullBaseUrl . pathname ) ;
80+ }
81+ }
82+
4383
4484 const RESERVED_WORDS = [ 'if' , 'in' , 'is' , '__cedar' ] ;
4585 const schemaNamespaceRegex = / ^ [ _ a - z A - Z ] [ _ a - z A - Z 0 - 9 ] * (?: : : (?: [ _ a - z A - Z ] [ _ a - z A - Z 0 - 9 ] * ) ) * $ / ;
@@ -100,6 +140,7 @@ export class Tools {
100140 if ( ! operationObject ) {
101141 continue ;
102142 }
143+ const httpPathTemplateWithBasePath = `${ basePath } ${ httpPathTemplate } ` ;
103144 const { actionName, actionDefinition} = Tools . generateActionDefinitionFromOperationObject (
104145 httpVerb ,
105146 httpPathTemplate ,
@@ -112,7 +153,7 @@ export class Tools {
112153 ...actionDefinition ,
113154 annotations : {
114155 httpVerb,
115- httpPathTemplate,
156+ httpPathTemplate : httpPathTemplateWithBasePath ,
116157 }
117158 } ,
118159
@@ -231,6 +272,8 @@ export class Tools {
231272 cedarExtension . appliesToResourceTypes . every ( ( value ) => typeof value === 'string' ) ;
232273 if ( isValidValue ) {
233274 resourceTypes = cedarExtension . appliesToResourceTypes ;
275+ } else {
276+ throw new Error ( `Invalid x-cedar extension in operation definition for ${ httpVerb } ${ httpPathTemplate } ` ) ;
234277 }
235278 }
236279 let attributes = { } ;
0 commit comments