@weclapp/sdk 2.0.0-dev.50 → 2.0.0-dev.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +407 -355
  2. package/package.json +10 -6
package/dist/cli.js CHANGED
@@ -4,7 +4,7 @@ import { rollup } from 'rollup';
4
4
  import terser from '@rollup/plugin-terser';
5
5
  import ts from '@rollup/plugin-typescript';
6
6
  import indentString from 'indent-string';
7
- import { snakeCase, camelCase, pascalCase } from 'change-case';
7
+ import { snakeCase, pascalCase, camelCase } from 'change-case';
8
8
  import chalk from 'chalk';
9
9
  import { OpenAPIV3 } from 'openapi-types';
10
10
  import { createHash } from 'crypto';
@@ -157,8 +157,8 @@ const resolveImports = (target) => {
157
157
  };
158
158
  const resolveMappings = (target) => `const wrapResponse = ${isRXTarget(target) ? 'defer' : '(v: (...args: any[]) => any) => v()'};`;
159
159
  const resolveBinaryClass = (target) => `const resolveBinaryObject = () => ${resolveBinaryType(target)};`;
160
- const generateBase = (target, apiVersion, options) => {
161
- return generateStatements(resolveImports(target), `const apiVersion = ${apiVersion}`, resolveMappings(target), resolveBinaryClass(target), globalConfig, types, utils, root, options.useQueryLanguage ? queriesWithQueryLanguage : queriesWithFilter, options.generateUnique ? unique : '', multiRequest);
160
+ const generateBase = (apiVersion, { target, useQueryLanguage, generateUnique }) => {
161
+ return generateStatements(resolveImports(target), `const apiVersion = ${apiVersion}`, resolveMappings(target), resolveBinaryClass(target), globalConfig, types, utils, root, useQueryLanguage ? queriesWithQueryLanguage : queriesWithFilter, generateUnique ? unique : '', multiRequest);
162
162
  };
163
163
 
164
164
  const transformKey = (s) => snakeCase(s).toUpperCase();
@@ -191,22 +191,10 @@ const isArraySchemaObject = (v) => {
191
191
  const isResponseObject = (v) => {
192
192
  return isObject(v) && typeof v.description === 'string';
193
193
  };
194
- const isNonArraySchemaObject = (v) => {
195
- return isObject(v) && ['string', 'undefined'].includes(typeof v.type);
196
- };
197
- const isFilterPathsSchemaObject = (v) => {
198
- return isObject(v) && v.type === 'object' && isObject(v['x-weclapp-filterPaths']);
199
- };
200
- const isFilterPropertySchemaObject = (v) => {
201
- return isObject(v) && v.type === 'object' && isObject(v['x-weclapp-filterProperties']);
202
- };
203
- const isRelatedEntitySchema = (v) => {
204
- return isObject(v) && isNonArraySchemaObject(v) && 'x-weclapp' in v && isObject(v['x-weclapp']);
205
- };
206
194
 
207
- const generateEnums = (schemas) => {
195
+ const generateEnums = (context) => {
208
196
  const enums = new Map();
209
- for (const [schemaName, schema] of schemas) {
197
+ for (const [schemaName, schema] of context.schemas) {
210
198
  if (isEnumSchemaObject(schema)) {
211
199
  const enumName = loosePascalCase(schemaName);
212
200
  if (!enums.has(enumName)) {
@@ -232,6 +220,23 @@ const concat = (strings, separator = ', ', maxLength = 80) => {
232
220
  }
233
221
  };
234
222
 
223
+ const convertParametersToSchemaObject = (parameters) => {
224
+ const properties = [];
225
+ const required = [];
226
+ for (const param of parameters) {
227
+ if (param.in === 'query' && param.schema) {
228
+ properties.push([param.name, param.schema]);
229
+ if (param.required)
230
+ required.push(param.name);
231
+ }
232
+ }
233
+ return {
234
+ type: 'object',
235
+ properties: Object.fromEntries(properties),
236
+ required
237
+ };
238
+ };
239
+
235
240
  const createReferenceType = (value) => ({
236
241
  type: 'reference',
237
242
  toString: () => loosePascalCase(value)
@@ -274,7 +279,10 @@ const getRefName = (obj) => {
274
279
  return obj.$ref.replace(/.*\//, '');
275
280
  };
276
281
  const convertToTypeScriptType = (schema) => {
277
- if (isReferenceObject(schema)) {
282
+ if (Array.isArray(schema)) {
283
+ return convertToTypeScriptType(convertParametersToSchemaObject(schema));
284
+ }
285
+ else if (isReferenceObject(schema)) {
278
286
  return createReferenceType(getRefName(schema));
279
287
  }
280
288
  else {
@@ -293,7 +301,7 @@ const convertToTypeScriptType = (schema) => {
293
301
  return createRawType('boolean');
294
302
  case 'object': {
295
303
  const { properties = {}, required = [] } = schema;
296
- return createObjectType(Object.fromEntries(Object.entries(properties).map((v) => [v[0], convertToTypeScriptType(v[1])])), required);
304
+ return createObjectType(Object.fromEntries(Object.entries(properties).map(([prop, propSchema]) => [prop, convertToTypeScriptType(propSchema)])), required);
297
305
  }
298
306
  case 'array':
299
307
  return createArrayType(convertToTypeScriptType(schema.items));
@@ -303,40 +311,43 @@ const convertToTypeScriptType = (schema) => {
303
311
  }
304
312
  };
305
313
 
306
- const setEntityEnumProperty = (enums, prop, meta) => {
314
+ const setReferenceMeta = (prop, metaData, context) => {
307
315
  const referenceName = getRefName(prop);
308
- const enumName = loosePascalCase(referenceName);
309
- if (enums.has(enumName)) {
310
- meta.enum = enumName;
316
+ const referenceSchema = context.schemas.get(referenceName);
317
+ if (isEnumSchemaObject(referenceSchema)) {
318
+ metaData.enum = loosePascalCase(referenceName);
311
319
  }
312
320
  else {
313
- meta.entity = referenceName;
321
+ metaData.entity = referenceName;
314
322
  }
315
323
  };
316
- const extractPropertyMetaData = (enums, meta, prop) => {
317
- const result = {
318
- service: meta.service,
319
- entity: meta.entity
320
- };
324
+ const extractPropertyMetaData = (prop, context) => {
325
+ const metaData = {};
326
+ const weclappExtension = prop['x-weclapp'];
327
+ if (weclappExtension) {
328
+ metaData.service = weclappExtension.service;
329
+ metaData.entity = weclappExtension.entity;
330
+ }
321
331
  if (isReferenceObject(prop)) {
322
- setEntityEnumProperty(enums, prop, result);
323
- result.type = 'reference';
324
- return result;
325
- }
326
- result.type = prop.type;
327
- result.format = prop.format;
328
- result.maxLength = prop.maxLength;
329
- result.pattern = prop.pattern;
330
- if (isArraySchemaObject(prop)) {
331
- if (isReferenceObject(prop.items)) {
332
- setEntityEnumProperty(enums, prop.items, result);
333
- result.format = 'reference';
334
- }
335
- else {
336
- result.format = 'string';
332
+ metaData.type = 'reference';
333
+ setReferenceMeta(prop, metaData, context);
334
+ }
335
+ else {
336
+ metaData.type = prop.type;
337
+ metaData.format = prop.format;
338
+ metaData.maxLength = prop.maxLength;
339
+ metaData.pattern = prop.pattern;
340
+ if (isArraySchemaObject(prop)) {
341
+ if (isReferenceObject(prop.items)) {
342
+ metaData.format = 'reference';
343
+ setReferenceMeta(prop.items, metaData, context);
344
+ }
345
+ else {
346
+ metaData.format = 'string';
347
+ }
337
348
  }
338
349
  }
339
- return result;
350
+ return metaData;
340
351
  };
341
352
 
342
353
  const generateInlineComment = (comment) => `/** ${comment} */`;
@@ -383,108 +394,6 @@ const generateInterfaceType = (name, entries, extend) => {
383
394
  return generateType(name, typeDefinition);
384
395
  };
385
396
 
386
- const FILTER_PROPS_SUFFIX = 'Filter_Props';
387
- const generateEntities = (schemas, enums) => {
388
- const entities = new Map();
389
- for (const [schemaName, schema] of schemas) {
390
- // Enums are generated separately
391
- if (isEnumSchemaObject(schema)) {
392
- continue;
393
- }
394
- const entityInterfaceName = loosePascalCase(schemaName);
395
- let parentEntityInterfaceName = undefined;
396
- const entityInterfaceProperties = [];
397
- const filterableInterfaceProperties = [];
398
- const properties = new Map();
399
- const processProperties = (props = {}, isXweclappFilterProp) => {
400
- for (const [name, property] of Object.entries(props)) {
401
- const meta = isRelatedEntitySchema(property) ? property['x-weclapp'] : {};
402
- const type = convertToTypeScriptType(property).toString();
403
- const comment = isNonArraySchemaObject(property)
404
- ? property.deprecated
405
- ? '@deprecated will be removed.'
406
- : property.format
407
- ? `format: ${property.format}`
408
- : undefined
409
- : undefined;
410
- if (meta.filterable !== false) {
411
- filterableInterfaceProperties.push({ name, type });
412
- }
413
- if (!isXweclappFilterProp) {
414
- entityInterfaceProperties.push({
415
- name,
416
- type,
417
- comment,
418
- required: meta.required,
419
- filterable: meta.filterable ?? true,
420
- readonly: !isReferenceObject(property) && property.readOnly
421
- });
422
- properties.set(name, extractPropertyMetaData(enums, meta, property));
423
- }
424
- }
425
- };
426
- if (schema.allOf?.length) {
427
- for (const item of schema.allOf) {
428
- if (isReferenceObject(item)) {
429
- parentEntityInterfaceName = convertToTypeScriptType(item).toString();
430
- }
431
- else if (isObjectSchemaObject(item)) {
432
- processProperties(item.properties);
433
- if (isFilterPropertySchemaObject(item)) {
434
- processProperties(item['x-weclapp-filterProperties'], true);
435
- }
436
- if (isFilterPathsSchemaObject(item)) {
437
- const fPaths = item['x-weclapp-filterPaths'];
438
- for (const path in fPaths) {
439
- if (!path.includes('.')) {
440
- filterableInterfaceProperties.push({ name: path, type: fPaths[path] });
441
- }
442
- }
443
- }
444
- }
445
- }
446
- }
447
- processProperties(schema.properties);
448
- entities.set(schemaName, {
449
- name: schemaName,
450
- properties,
451
- filterableInterfaceProperties: filterableInterfaceProperties.sort((propA, propB) => propA.name.localeCompare(propB.name)),
452
- parentName: parentEntityInterfaceName ? camelCase(parentEntityInterfaceName) : undefined,
453
- source: generateStatements(generateInterface(entityInterfaceName, entityInterfaceProperties, parentEntityInterfaceName))
454
- });
455
- }
456
- return entities;
457
- };
458
- const generateEntityFilterProps = (entities, enums) => {
459
- const entityFilterProps = new Map();
460
- const transformFilterProps = (props) => {
461
- return props.map((prop) => {
462
- if (!prop.type ||
463
- enums.has(prop.type) ||
464
- prop.type === 'string' ||
465
- prop.type === 'number' ||
466
- prop.type === 'boolean' ||
467
- prop.type === '{}' ||
468
- prop.type.endsWith('[]') ||
469
- prop.type.includes("'")) {
470
- return prop;
471
- }
472
- return { ...prop, type: `${pascalCase(prop.type)}_${FILTER_PROPS_SUFFIX}` };
473
- });
474
- };
475
- entities.forEach((entity, name) => {
476
- const entityFilterName = `${pascalCase(name)}_${FILTER_PROPS_SUFFIX}`;
477
- const parentName = entity.parentName ? `${pascalCase(entity.parentName)}_${FILTER_PROPS_SUFFIX}` : undefined;
478
- const filterableInterfaceProperties = transformFilterProps(entity.filterableInterfaceProperties);
479
- entityFilterProps.set(entityFilterName, {
480
- name: entityFilterName,
481
- parentName,
482
- source: generateStatements(generateInterface(entityFilterName, filterableInterfaceProperties, parentName))
483
- });
484
- });
485
- return entityFilterProps;
486
- };
487
-
488
397
  /**
489
398
  * Pluralizes a word, most of the time correct.
490
399
  * @param s String to pluralize.
@@ -554,6 +463,108 @@ const logger = new (class {
554
463
  }
555
464
  })();
556
465
 
466
+ const FILTER_PROPS_SUFFIX = 'Filter_Props';
467
+ const generateEntities = (context) => {
468
+ const entities = new Map();
469
+ for (const [schemaName, schema] of context.schemas) {
470
+ // Enums are generated separately
471
+ if (isEnumSchemaObject(schema)) {
472
+ continue;
473
+ }
474
+ const entityName = schemaName;
475
+ const entityInterfaceName = loosePascalCase(entityName);
476
+ const entityInterfaceProperties = [];
477
+ const properties = new Map();
478
+ let parentEntityName = undefined;
479
+ let parentEntityInterfaceName = undefined;
480
+ const entityFilterInterfaceName = `${entityInterfaceName}_${FILTER_PROPS_SUFFIX}`;
481
+ const entityFilterInterfaceProperties = [];
482
+ let parentEntityFilterInterfaceName = undefined;
483
+ const processProperties = (props = {}) => {
484
+ for (const [propertyName, propertySchema] of Object.entries(props)) {
485
+ const weclappExtension = propertySchema['x-weclapp'];
486
+ properties.set(propertyName, extractPropertyMetaData(propertySchema, context));
487
+ const type = convertToTypeScriptType(propertySchema).toString();
488
+ // cast to SchemaObject to access deprecated and readOnly properties (ReferenceObject can also include these props in OpenAPI 3.1)
489
+ const castedSchema = propertySchema;
490
+ const comment = castedSchema.deprecated
491
+ ? '@deprecated will be removed.'
492
+ : castedSchema.format
493
+ ? `format: ${castedSchema.format}`
494
+ : undefined;
495
+ if (weclappExtension?.filterable !== false) {
496
+ entityFilterInterfaceProperties.push({ name: propertyName, type, comment });
497
+ }
498
+ entityInterfaceProperties.push({
499
+ name: propertyName,
500
+ type,
501
+ required: weclappExtension?.required,
502
+ readonly: castedSchema.readOnly,
503
+ comment
504
+ });
505
+ }
506
+ };
507
+ const processExtraFilterProperties = (props = {}) => {
508
+ for (const [propertyName, propertySchema] of Object.entries(props)) {
509
+ if (isReferenceObject(propertySchema))
510
+ continue;
511
+ const type = convertToTypeScriptType(propertySchema).toString();
512
+ const comment = propertySchema.deprecated
513
+ ? '@deprecated will be removed.'
514
+ : propertySchema.format
515
+ ? `format: ${propertySchema.format}`
516
+ : undefined;
517
+ entityFilterInterfaceProperties.push({ name: propertyName, type, comment });
518
+ }
519
+ };
520
+ const processFilterPaths = (filterPaths = {}) => {
521
+ for (const [filterProp, entityName] of Object.entries(filterPaths)) {
522
+ if (!filterProp.includes('.') && context.schemas.get(entityName)) {
523
+ entityFilterInterfaceProperties.push({
524
+ name: filterProp,
525
+ type: `${loosePascalCase(entityName)}_${FILTER_PROPS_SUFFIX}`
526
+ });
527
+ }
528
+ }
529
+ };
530
+ if (schema.allOf?.length) {
531
+ if (schema.allOf.length > 2) {
532
+ logger.errorLn(`Failed to process schema for ${schemaName}: invalid allOf length`);
533
+ continue;
534
+ }
535
+ for (const item of schema.allOf) {
536
+ if (isReferenceObject(item)) {
537
+ parentEntityName = getRefName(item);
538
+ parentEntityInterfaceName = createReferenceType(parentEntityName).toString();
539
+ parentEntityFilterInterfaceName = `${parentEntityInterfaceName}_${FILTER_PROPS_SUFFIX}`;
540
+ }
541
+ else if (item.type === 'object') {
542
+ processProperties(item.properties);
543
+ processExtraFilterProperties(item['x-weclapp-filterProperties']);
544
+ processFilterPaths(item['x-weclapp-filterPaths']);
545
+ }
546
+ else {
547
+ logger.errorLn(`Failed to process schema for ${schemaName}: invalid schema type in allOf`);
548
+ }
549
+ }
550
+ }
551
+ else {
552
+ processProperties(schema.properties);
553
+ }
554
+ entities.set(entityName, {
555
+ name: entityName,
556
+ interfaceName: entityInterfaceName,
557
+ properties,
558
+ source: generateStatements(generateInterface(entityInterfaceName, entityInterfaceProperties, parentEntityInterfaceName)),
559
+ filterInterfaceName: entityFilterInterfaceName,
560
+ filterSource: generateStatements(generateInterface(entityFilterInterfaceName, entityFilterInterfaceProperties, parentEntityFilterInterfaceName)),
561
+ parentName: parentEntityName,
562
+ parentInterfaceName: parentEntityInterfaceName
563
+ });
564
+ }
565
+ return entities;
566
+ };
567
+
557
568
  /**
558
569
  * ROOT => /article
559
570
  * COUNT => /article/count
@@ -600,6 +611,31 @@ const parseEndpointPath = (path) => {
600
611
  }
601
612
  return undefined;
602
613
  };
614
+ const isMultiPartUploadPath = (path) => {
615
+ const [, entity, ...rest] = path.split('/');
616
+ return entity && rest.length === 2 && rest[1] === 'multipartUpload';
617
+ };
618
+ const parseEndpointsPaths = (paths) => {
619
+ const endpoints = new Map();
620
+ for (const [rawPath, path] of Object.entries(paths)) {
621
+ const endpoint = parseEndpointPath(rawPath);
622
+ if (!endpoint || !path) {
623
+ // Todo: Should be removed if sdk supports multi part upload.
624
+ if (isMultiPartUploadPath(rawPath)) {
625
+ continue;
626
+ }
627
+ logger.errorLn(`Failed to parse ${rawPath}`);
628
+ continue;
629
+ }
630
+ if (endpoints.has(endpoint.service)) {
631
+ endpoints.get(endpoint.service)?.push({ endpoint, path });
632
+ }
633
+ else {
634
+ endpoints.set(endpoint.service, [{ endpoint, path }]);
635
+ }
636
+ }
637
+ return endpoints;
638
+ };
603
639
 
604
640
  const generateArrowFunction = ({ name, signature, returns, params }) => {
605
641
  return `const ${name}: ${signature} = (${params?.join(', ') ?? ''}) =>\n${indent(returns)};`;
@@ -611,43 +647,40 @@ const generateArrowFunctionType = ({ type, returns = 'void', generics, params })
611
647
  return generateType(type, `${genericsString + paramsString} =>\n${indent(returns)}`);
612
648
  };
613
649
 
614
- const convertParametersToSchema = (parameters = []) => {
615
- const properties = [];
616
- const required = [];
617
- for (const param of parameters) {
618
- if (isParameterObject(param) && param.in === 'query') {
619
- if (param.schema) {
620
- properties.push([param.name, param.schema]);
621
- if (param.required)
622
- required.push(param.name);
623
- }
650
+ const resolveParameters = (resolvableParameters = [], parameters) => {
651
+ if (!resolvableParameters)
652
+ return [];
653
+ return resolvableParameters.flatMap((param) => {
654
+ if (isReferenceObject(param)) {
655
+ const resolved = parameters.get(getRefName(param));
656
+ return resolved ? [resolved] : [];
624
657
  }
625
- }
626
- return {
627
- type: 'object',
628
- required,
629
- properties: Object.fromEntries(properties)
630
- };
658
+ return [param];
659
+ });
631
660
  };
632
661
 
633
- const generateCountEndpoint = ({ aliases, path, target, endpoint }) => {
662
+ const generateCountEndpoint = ({ endpoint, operationObject, entities, context, options }) => {
634
663
  const functionName = 'count';
635
664
  const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
636
- const entity = aliases.get(endpoint.service) ?? pascalCase(endpoint.service);
665
+ const relatedEntityName = context.aliases.get(endpoint.service);
666
+ const relatedEntity = !!relatedEntityName && entities.get(relatedEntityName);
667
+ if (!relatedEntity) {
668
+ throw Error(`Related entity schema for service ${endpoint.service} not found`);
669
+ }
637
670
  const parametersTypeName = `${functionTypeName}_Parameters`;
638
671
  const parametersType = createObjectType({
639
- params: convertToTypeScriptType(convertParametersToSchema(path.parameters))
672
+ params: convertToTypeScriptType(resolveParameters(operationObject.parameters, context.parameters))
640
673
  });
641
674
  const parametersTypeSource = generateInterfaceFromObject(parametersTypeName, parametersType, 'propagate');
642
675
  const filterTypeName = `${functionTypeName}_Filter`;
643
- const filterTypeSource = generateInterfaceType(filterTypeName, [], [`${entity}_${FILTER_PROPS_SUFFIX}`]);
676
+ const filterTypeSource = generateInterfaceType(filterTypeName, [], [`${relatedEntity.filterInterfaceName}`]);
644
677
  const functionTypeSource = generateArrowFunctionType({
645
678
  type: functionTypeName,
646
679
  params: [
647
- `query${parametersType.isFullyOptional() ? '?' : ''}: CountQuery<${filterTypeName}>${path.parameters?.length ? ' & ' + parametersTypeName : ''}`,
680
+ `query${parametersType.isFullyOptional() ? '?' : ''}: CountQuery<${filterTypeName}>${operationObject.parameters?.length ? ' & ' + parametersTypeName : ''}`,
648
681
  'requestOptions?: RequestOptions'
649
682
  ],
650
- returns: `${resolveResponseType(target)}<number>`
683
+ returns: `${resolveResponseType(options.target)}<number>`
651
684
  });
652
685
  const functionSource = generateArrowFunction({
653
686
  name: functionName,
@@ -656,44 +689,51 @@ const generateCountEndpoint = ({ aliases, path, target, endpoint }) => {
656
689
  params: ['query', 'requestOptions?: RequestOptions']
657
690
  });
658
691
  return {
659
- entity,
660
692
  name: functionName,
661
693
  type: { name: functionTypeName, source: functionTypeSource },
662
694
  func: { name: functionName, source: functionSource },
663
695
  interfaces: [
664
- ...(path.parameters?.length ? [{ name: parametersTypeName, source: parametersTypeSource }] : []),
696
+ ...(operationObject.parameters?.length ? [{ name: parametersTypeName, source: parametersTypeSource }] : []),
665
697
  { name: filterTypeName, source: filterTypeSource }
666
698
  ]
667
699
  };
668
700
  };
669
701
 
670
- const generateBodyType = (body) => {
671
- if (isReferenceObject(body)) {
672
- return convertToTypeScriptType(body);
673
- }
702
+ const generateContentType = (body, fallback = 'unknown') => {
703
+ if (!body?.content)
704
+ return createRawType(fallback);
674
705
  const types = [];
675
- for (const { schema } of Object.values(body?.content ?? {})) {
706
+ for (const { schema } of Object.values(body.content)) {
676
707
  if (schema) {
677
708
  types.push(convertToTypeScriptType(schema));
678
709
  }
679
710
  }
680
- return types.length ? (types.length === 1 ? types[0] : createTupleType(types)) : undefined;
711
+ return (types.length > 1 ? createTupleType(types) : types[0]) ?? createRawType(fallback);
681
712
  };
682
713
 
683
- const generateRequestBodyType = ({ requestBody }) => {
684
- return generateBodyType(requestBody) ?? createRawType('unknown');
714
+ const generateRequestBodyType = ({ requestBody }, requestBodies) => {
715
+ const requestBodyObject = requestBody && isReferenceObject(requestBody) ? requestBodies.get(getRefName(requestBody)) : requestBody;
716
+ return generateContentType(requestBodyObject);
685
717
  };
686
718
 
687
- const resolveBodyType = ({ responses }) => Object.entries(responses).filter((v) => v[0].startsWith('2'))[0]?.[1];
688
- const generateResponseBodyType = (object) => generateBodyType(resolveBodyType(object)) ?? createRawType('void');
719
+ const resolveResponsesObject = (responses) => Object.entries(responses).find(([statusCode]) => statusCode.startsWith('2'))?.[1];
720
+
721
+ const generateResponseType = (operationObject, responses) => {
722
+ const response = resolveResponsesObject(operationObject.responses);
723
+ const responseObject = response && isReferenceObject(response) ? responses.get(getRefName(response)) : response;
724
+ return generateContentType(responseObject, 'void');
725
+ };
689
726
 
690
- const generateCreateEndpoint = ({ target, path, endpoint }) => {
727
+ const generateCreateEndpoint = ({ endpoint, operationObject, context, options }) => {
691
728
  const functionName = 'create';
692
729
  const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
693
730
  const functionTypeSource = generateArrowFunctionType({
694
731
  type: functionTypeName,
695
- params: [`data: DeepPartial<${generateRequestBodyType(path).toString()}>`, 'requestOptions?: RequestOptions'],
696
- returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`
732
+ params: [
733
+ `data: DeepPartial<${generateRequestBodyType(operationObject, context.requestBodies).toString()}>`,
734
+ 'requestOptions?: RequestOptions'
735
+ ],
736
+ returns: `${resolveResponseType(options.target)}<${generateResponseType(operationObject, context.responses).toString()}>`
697
737
  });
698
738
  const functionSource = generateArrowFunction({
699
739
  name: functionName,
@@ -702,7 +742,6 @@ const generateCreateEndpoint = ({ target, path, endpoint }) => {
702
742
  params: ['data', 'requestOptions?: RequestOptions']
703
743
  });
704
744
  return {
705
- entity: pascalCase(endpoint.service),
706
745
  name: functionName,
707
746
  type: { name: functionTypeName, source: functionTypeSource },
708
747
  func: { name: functionName, source: functionSource }
@@ -720,16 +759,19 @@ const insertPathPlaceholder = (path, record) => {
720
759
  const wrapBody = (type, target) => {
721
760
  return type.toString() === 'binary' ? createRawType(isNodeTarget(target) ? 'BodyInit' : 'Blob') : type; // node-fetch returns a Blob as well
722
761
  };
723
- const generateGenericEndpoint = (suffix) => ({ target, method, path, endpoint }) => {
762
+ const generateGenericEndpoint = (suffix) => ({ method, endpoint, operationObject, context, options }) => {
724
763
  const functionName = generateGenericFunctionName(endpoint.path, suffix, method);
725
764
  const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
726
765
  const entityQuery = `${functionTypeName}_Query`;
727
766
  const hasId = endpoint.path.includes('{id}');
728
767
  const params = createObjectType({
729
- params: convertToTypeScriptType(convertParametersToSchema(path.parameters)),
730
- body: method === 'get' ? undefined : wrapBody(generateRequestBodyType(path), target)
768
+ params: operationObject.parameters &&
769
+ convertToTypeScriptType(resolveParameters(operationObject.parameters, context.parameters)),
770
+ body: method === 'get'
771
+ ? undefined
772
+ : wrapBody(generateRequestBodyType(operationObject, context.requestBodies), options.target)
731
773
  });
732
- const responseBody = generateResponseBodyType(path);
774
+ const responseBody = generateResponseType(operationObject, context.responses);
733
775
  const functionTypeSource = generateArrowFunctionType({
734
776
  type: functionTypeName,
735
777
  params: [
@@ -737,7 +779,7 @@ const generateGenericEndpoint = (suffix) => ({ target, method, path, endpoint })
737
779
  `query${params.isFullyOptional() ? '?' : ''}: ${entityQuery}`,
738
780
  'requestOptions?: RequestOptions'
739
781
  ],
740
- returns: `${resolveResponseType(target)}<${wrapBody(responseBody, target).toString('force')}>`
782
+ returns: `${resolveResponseType(options.target)}<${wrapBody(responseBody, options.target).toString('force')}>`
741
783
  });
742
784
  const functionSource = generateArrowFunction({
743
785
  name: functionName,
@@ -746,7 +788,6 @@ const generateGenericEndpoint = (suffix) => ({ target, method, path, endpoint })
746
788
  returns: `_generic(cfg, ${generateString(method.toUpperCase())}, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, query, ${String(responseBody.toString() === 'binary')}, requestOptions)`
747
789
  });
748
790
  return {
749
- entity: pascalCase(endpoint.service),
750
791
  name: functionName,
751
792
  type: { name: functionTypeName, source: functionTypeSource },
752
793
  func: { name: functionName, source: functionSource },
@@ -759,13 +800,13 @@ const generateGenericEndpoint = (suffix) => ({ target, method, path, endpoint })
759
800
  };
760
801
  };
761
802
 
762
- const generateRemoveEndpoint = ({ target, endpoint }) => {
803
+ const generateRemoveEndpoint = ({ endpoint, options }) => {
763
804
  const functionName = 'remove';
764
805
  const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
765
806
  const functionTypeSource = generateArrowFunctionType({
766
807
  type: functionTypeName,
767
808
  params: ['id: string', 'options?: RemoveQuery', 'requestOptions?: RequestOptions'],
768
- returns: `${resolveResponseType(target)}<void>`
809
+ returns: `${resolveResponseType(options.target)}<void>`
769
810
  });
770
811
  const functionSource = generateArrowFunction({
771
812
  name: functionName,
@@ -774,7 +815,6 @@ const generateRemoveEndpoint = ({ target, endpoint }) => {
774
815
  params: ['id', 'options?: RemoveQuery', 'requestOptions?: RequestOptions']
775
816
  });
776
817
  return {
777
- entity: pascalCase(endpoint.service),
778
818
  name: functionName,
779
819
  type: { name: functionTypeName, source: functionTypeSource },
780
820
  func: { name: functionName, source: functionSource }
@@ -790,8 +830,8 @@ const excludedParameters = [
790
830
  'includeReferencedEntities',
791
831
  'additionalProperties'
792
832
  ];
793
- const resolveAdditionalPropertiesSchema = (path) => {
794
- const body = resolveBodyType(path);
833
+ const resolveAdditionalPropertiesSchema = ({ responses }) => {
834
+ const body = resolveResponsesObject(responses);
795
835
  if (isResponseObject(body)) {
796
836
  const schema = body?.content?.['application/json']?.schema;
797
837
  if (isObjectSchemaObject(schema)) {
@@ -827,12 +867,14 @@ const resolveReferencedEntities = (entity, entities) => {
827
867
  const generatedEntity = entities.get(entity);
828
868
  if (generatedEntity) {
829
869
  for (const [, propertyMetaData] of generatedEntity.properties) {
830
- if (propertyMetaData.entity && propertyMetaData.service) {
831
- referencedEntities.push({
832
- name: propertyMetaData.service,
833
- type: `${pascalCase(propertyMetaData.entity)}[]`,
834
- required: true
835
- });
870
+ if (propertyMetaData.service && propertyMetaData.entity) {
871
+ const referencedEntity = entities.get(propertyMetaData.entity);
872
+ if (referencedEntity)
873
+ referencedEntities.push({
874
+ name: propertyMetaData.service,
875
+ type: `${referencedEntity.interfaceName}[]`,
876
+ required: true
877
+ });
836
878
  }
837
879
  }
838
880
  if (generatedEntity.parentName) {
@@ -841,33 +883,37 @@ const resolveReferencedEntities = (entity, entities) => {
841
883
  }
842
884
  return referencedEntities;
843
885
  };
844
- const generateSomeEndpoint = ({ endpoint, target, path, entities, aliases }) => {
886
+ const generateSomeEndpoint = ({ endpoint, operationObject, entities, context, options }) => {
845
887
  const functionName = 'some';
846
888
  const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
847
- const entity = aliases.get(endpoint.service) ?? pascalCase(endpoint.service);
889
+ const relatedEntityName = context.aliases.get(endpoint.service);
890
+ const relatedEntity = !!relatedEntityName && entities.get(relatedEntityName);
891
+ if (!relatedEntity) {
892
+ throw Error(`Related entity schema for service ${endpoint.service} not found`);
893
+ }
848
894
  const parametersTypeName = `${functionTypeName}_Parameters`;
849
- const parameters = path.parameters?.filter((v) => (isParameterObject(v) ? !excludedParameters.includes(v.name) : false)) ?? [];
895
+ const parameters = operationObject.parameters?.filter((v) => isParameterObject(v) ? !excludedParameters.includes(v.name) : false);
850
896
  const parametersType = createObjectType({
851
- params: convertToTypeScriptType(convertParametersToSchema(parameters))
897
+ params: parameters && convertToTypeScriptType(resolveParameters(parameters, context.parameters))
852
898
  });
853
899
  const parametersTypeSource = generateInterfaceFromObject(parametersTypeName, parametersType, 'propagate');
854
900
  const filterTypeName = `${functionTypeName}_Filter`;
855
- const filterTypeSource = generateInterfaceType(filterTypeName, [], [`${entity}_${FILTER_PROPS_SUFFIX}`]);
901
+ const filterTypeSource = generateInterfaceType(filterTypeName, [], [`${relatedEntity.filterInterfaceName}`]);
856
902
  const referencesTypeName = `${functionTypeName}_References`;
857
903
  const referencesTypeSource = generateInterfaceType(referencesTypeName, resolveReferences(endpoint.service, entities));
858
904
  const additionalPropertyTypeName = `${functionTypeName}_AdditionalProperty`;
859
905
  const additionalPropertyTypeSource = generateType(additionalPropertyTypeName, 'string');
860
906
  const queryTypeName = `${functionTypeName}_Query`;
861
- const queryTypeSource = generateType(queryTypeName, `SomeQuery<${entity}, ${filterTypeName}, ${referencesTypeName}, ${additionalPropertyTypeName}> & ${parametersTypeName}`);
907
+ const queryTypeSource = generateType(queryTypeName, `SomeQuery<${relatedEntity.interfaceName}, ${filterTypeName}, ${referencesTypeName}, ${additionalPropertyTypeName}> & ${parametersTypeName}`);
862
908
  const referencedEntitiesTypeName = `${functionTypeName}_ReferencedEntities`;
863
909
  const referencedEntitiesTypeSource = generateInterfaceType(referencedEntitiesTypeName, resolveReferencedEntities(endpoint.service, entities));
864
910
  const additionalPropertiesTypeName = `${functionTypeName}_AdditionalProperties`;
865
- const additionalPropertiesSchema = resolveAdditionalPropertiesSchema(path);
911
+ const additionalPropertiesSchema = resolveAdditionalPropertiesSchema(operationObject);
866
912
  const additionalPropertiesTypeSource = generateType(additionalPropertiesTypeName, additionalPropertiesSchema ? convertToTypeScriptType(additionalPropertiesSchema).toString() : '{}');
867
913
  const functionTypeSource = generateArrowFunctionType({
868
914
  type: functionTypeName,
869
915
  params: [`query${parametersType.isFullyOptional() ? '?' : ''}: ${queryTypeName}, requestOptions?: RequestOptions`],
870
- returns: `${resolveResponseType(target)}<SomeQueryReturn<${entity}, ${referencedEntitiesTypeName}, ${additionalPropertiesTypeName}>>`
916
+ returns: `${resolveResponseType(options.target)}<SomeQueryReturn<${relatedEntity.interfaceName}, ${referencedEntitiesTypeName}, ${additionalPropertiesTypeName}>>`
871
917
  });
872
918
  const functionSource = generateArrowFunction({
873
919
  name: functionName,
@@ -876,7 +922,6 @@ const generateSomeEndpoint = ({ endpoint, target, path, entities, aliases }) =>
876
922
  params: ['query', 'requestOptions?: RequestOptions']
877
923
  });
878
924
  return {
879
- entity,
880
925
  name: functionName,
881
926
  type: { name: functionTypeName, source: functionTypeSource },
882
927
  func: { name: functionName, source: functionSource },
@@ -892,14 +937,14 @@ const generateSomeEndpoint = ({ endpoint, target, path, entities, aliases }) =>
892
937
  };
893
938
  };
894
939
 
895
- const generateUniqueEndpoint = ({ target, path, endpoint }) => {
940
+ const generateUniqueEndpoint = ({ operationObject, endpoint, context, options }) => {
896
941
  const functionName = 'unique';
897
942
  const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
898
943
  const functionTypeSource = generateArrowFunctionType({
899
944
  type: functionTypeName,
900
945
  params: ['id: string', 'query?: Q', 'requestOptions?: RequestOptions'],
901
946
  generics: ['Q extends UniqueQuery'],
902
- returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`
947
+ returns: `${resolveResponseType(options.target)}<${generateResponseType(operationObject, context.responses).toString()}>`
903
948
  });
904
949
  const functionSource = generateArrowFunction({
905
950
  name: functionName,
@@ -908,25 +953,24 @@ const generateUniqueEndpoint = ({ target, path, endpoint }) => {
908
953
  returns: `_${functionName}(cfg, \`${insertPathPlaceholder(endpoint.path, { id: '${id}' })}\`, query, requestOptions)`
909
954
  });
910
955
  return {
911
- entity: pascalCase(endpoint.service),
912
956
  name: functionName,
913
957
  type: { name: functionTypeName, source: functionTypeSource },
914
958
  func: { name: functionName, source: functionSource }
915
959
  };
916
960
  };
917
961
 
918
- const generateUpdateEndpoint = ({ target, path, endpoint }) => {
962
+ const generateUpdateEndpoint = ({ endpoint, operationObject, context, options }) => {
919
963
  const functionName = 'update';
920
964
  const functionTypeName = `${pascalCase(endpoint.service)}Service_${pascalCase(functionName)}`;
921
965
  const functionTypeSource = generateArrowFunctionType({
922
966
  type: functionTypeName,
923
967
  params: [
924
968
  'id: string',
925
- `data: DeepPartial<${generateRequestBodyType(path).toString()}>`,
969
+ `data: DeepPartial<${generateRequestBodyType(operationObject, context.requestBodies).toString()}>`,
926
970
  'options?: UpdateQuery',
927
971
  'requestOptions?: RequestOptions'
928
972
  ],
929
- returns: `${resolveResponseType(target)}<${generateResponseBodyType(path).toString()}>`
973
+ returns: `${resolveResponseType(options.target)}<${generateResponseType(operationObject, context.responses).toString()}>`
930
974
  });
931
975
  const functionSource = generateArrowFunction({
932
976
  name: functionName,
@@ -935,102 +979,77 @@ const generateUpdateEndpoint = ({ target, path, endpoint }) => {
935
979
  params: ['id', 'data', 'options', 'requestOptions?: RequestOptions']
936
980
  });
937
981
  return {
938
- entity: pascalCase(endpoint.service),
939
982
  name: functionName,
940
983
  type: { name: functionTypeName, source: functionTypeSource },
941
984
  func: { name: functionName, source: functionSource }
942
985
  };
943
986
  };
944
987
 
945
- const isMultiPartUploadPath = (path) => {
946
- const [, entity, ...rest] = path.split('/');
947
- return entity && rest.length === 2 && rest[1] === 'multipartUpload';
948
- };
949
- const parseEndpointsAndGroupByEntity = (paths) => {
950
- const endpoints = new Map();
951
- for (const [rawPath, path] of Object.entries(paths)) {
952
- const endpoint = parseEndpointPath(rawPath);
953
- if (!endpoint || !path) {
954
- // Todo: Should be removed if sdk supports multi part upload.
955
- if (isMultiPartUploadPath(rawPath)) {
956
- continue;
957
- }
958
- logger.errorLn(`Failed to parse ${rawPath}`);
959
- continue;
960
- }
961
- if (endpoints.has(endpoint.service)) {
962
- endpoints.get(endpoint.service)?.push({ endpoint, path });
963
- }
964
- else {
965
- endpoints.set(endpoint.service, [{ endpoint, path }]);
966
- }
967
- }
968
- return endpoints;
969
- };
970
-
971
988
  const generators = {
972
989
  /* /article */
973
990
  [WeclappEndpointType.ROOT]: {
974
- get: generateSomeEndpoint,
975
- post: generateCreateEndpoint
991
+ [OpenAPIV3.HttpMethods.GET]: generateSomeEndpoint,
992
+ [OpenAPIV3.HttpMethods.POST]: generateCreateEndpoint
976
993
  },
977
994
  /* /article/count */
978
995
  [WeclappEndpointType.COUNT]: {
979
- get: generateCountEndpoint
996
+ [OpenAPIV3.HttpMethods.GET]: generateCountEndpoint
980
997
  },
981
998
  /* /article/:id */
982
999
  [WeclappEndpointType.ENTITY]: {
983
- get: generateUniqueEndpoint,
984
- delete: generateRemoveEndpoint,
985
- put: generateUpdateEndpoint
1000
+ [OpenAPIV3.HttpMethods.GET]: generateUniqueEndpoint,
1001
+ [OpenAPIV3.HttpMethods.PUT]: generateUpdateEndpoint,
1002
+ [OpenAPIV3.HttpMethods.DELETE]: generateRemoveEndpoint
986
1003
  },
987
1004
  /* /article/:id/method */
988
1005
  [WeclappEndpointType.GENERIC_ENTITY]: {
989
- get: generateGenericEndpoint('ById'),
990
- post: generateGenericEndpoint('ById')
1006
+ [OpenAPIV3.HttpMethods.GET]: generateGenericEndpoint('ById'),
1007
+ [OpenAPIV3.HttpMethods.POST]: generateGenericEndpoint('ById')
991
1008
  },
992
1009
  /* /article/method */
993
1010
  [WeclappEndpointType.GENERIC_ROOT]: {
994
- get: generateGenericEndpoint(),
995
- post: generateGenericEndpoint()
1011
+ [OpenAPIV3.HttpMethods.GET]: generateGenericEndpoint(),
1012
+ [OpenAPIV3.HttpMethods.POST]: generateGenericEndpoint()
996
1013
  }
997
1014
  };
998
- const generateServices = (paths, entities, aliases, options) => {
1015
+ const generateServices = (entities, context, options) => {
999
1016
  const services = new Map();
1000
- const endpoints = parseEndpointsAndGroupByEntity(paths);
1001
- for (const [serviceName, paths] of endpoints) {
1017
+ for (const [serviceName, serviceEndpoints] of context.endpoints) {
1002
1018
  const serviceFnName = camelCase(`${serviceName}Service`);
1003
1019
  const serviceTypeName = pascalCase(`${serviceName}Service`);
1004
1020
  const functions = [];
1005
- for (const { path, endpoint } of paths) {
1006
- const generator = generators[endpoint.type];
1007
- for (const [method, config] of Object.entries(path)) {
1008
- if ((method === 'get' && endpoint.type === WeclappEndpointType.ENTITY && !options.generateUnique) ||
1009
- (method === 'post' && (endpoint.type === WeclappEndpointType.COUNT || endpoint.path.endsWith('query')))) {
1021
+ for (const { path, endpoint } of serviceEndpoints) {
1022
+ for (const method of [
1023
+ OpenAPIV3.HttpMethods.GET,
1024
+ OpenAPIV3.HttpMethods.POST,
1025
+ OpenAPIV3.HttpMethods.PUT,
1026
+ OpenAPIV3.HttpMethods.DELETE
1027
+ ]) {
1028
+ if ((method === OpenAPIV3.HttpMethods.GET &&
1029
+ endpoint.type === WeclappEndpointType.ENTITY &&
1030
+ !options.generateUnique) ||
1031
+ (method === OpenAPIV3.HttpMethods.POST &&
1032
+ (endpoint.type === WeclappEndpointType.COUNT || endpoint.path.endsWith('query')))) {
1010
1033
  // Skip unique endpoints if generateUnique option is not set or if POST is used for filter queries
1011
1034
  continue;
1012
1035
  }
1013
- const generatorFn = generator[method];
1014
- if (generatorFn) {
1015
- const path = config;
1016
- const target = options.target;
1017
- if (!path.deprecated || options.deprecated) {
1036
+ const operationObject = path[method];
1037
+ const generatorFn = generators[endpoint.type][method];
1038
+ if (operationObject && generatorFn) {
1039
+ if (!operationObject.deprecated || options.deprecated) {
1018
1040
  functions.push({
1019
1041
  ...generatorFn({
1020
- endpoint,
1021
1042
  method,
1022
- target,
1023
- path,
1043
+ endpoint,
1044
+ operationObject,
1024
1045
  entities,
1025
- aliases
1046
+ context,
1047
+ options
1026
1048
  }),
1027
- path
1049
+ path: operationObject
1028
1050
  });
1029
1051
  }
1030
1052
  }
1031
- else {
1032
- logger.errorLn(`Failed to generate a function for ${method.toUpperCase()}:${endpoint.type} ${endpoint.path}`);
1033
- }
1034
1053
  }
1035
1054
  }
1036
1055
  if (!functions.length) {
@@ -1045,40 +1064,27 @@ const generateServices = (paths, entities, aliases, options) => {
1045
1064
  }))
1046
1065
  ])));
1047
1066
  const serviceFn = `export const ${serviceFnName} = (cfg?: ServiceConfig): ${serviceTypeName} => ${generateBlockStatements(...functions.map((v) => v.func.source), `return {${concat(functions.map((v) => v.func.name))}};`)};`;
1067
+ const relatedEntityName = context.aliases.get(serviceName);
1068
+ const relatedEntity = relatedEntityName ? entities.get(relatedEntityName) : undefined;
1048
1069
  services.set(serviceName, {
1049
1070
  name: serviceName,
1050
1071
  serviceFnName,
1051
- serviceTypeName,
1052
1072
  functions,
1053
1073
  source: generateStatements(serviceTypes, serviceFn),
1054
- deprecated: functions.every((v) => v.path.deprecated)
1074
+ deprecated: functions.every((v) => v.path.deprecated),
1075
+ relatedEntity
1055
1076
  });
1056
1077
  }
1057
1078
  return services;
1058
1079
  };
1059
1080
 
1060
- const generateCustomValueServices = (entities, services) => {
1061
- const customValueEntity = entities.get('customValue');
1081
+ const generateCustomValueServices = (services) => {
1062
1082
  const customValueEntities = [];
1063
- if (!customValueEntity) {
1064
- logger.warn('Cannot generate custom value utils, type not found.');
1065
- return '';
1066
- }
1067
- serviceLoop: for (const service of services) {
1068
- const someFunction = service.functions.find((v) => v.name === 'some');
1069
- if (!someFunction) {
1070
- continue;
1071
- }
1072
- const entity = entities.get(camelCase(someFunction.entity));
1073
- if (entity?.properties.size !== customValueEntity.properties.size) {
1074
- continue;
1075
- }
1076
- for (const [prop, { type }] of entity.properties) {
1077
- if (customValueEntity.properties.get(prop)?.type !== type) {
1078
- continue serviceLoop;
1079
- }
1083
+ for (const service of services) {
1084
+ const relatedEntity = service.relatedEntity;
1085
+ if (relatedEntity?.name === 'customValue') {
1086
+ customValueEntities.push(service.name);
1080
1087
  }
1081
- customValueEntities.push(service.name);
1082
1088
  }
1083
1089
  return generateStatements(generateType('WCustomValueService', concat(generateStrings(customValueEntities), ' | ')), `export const wCustomValueServiceNames: WCustomValueService[] = [${concat(generateStrings(customValueEntities))}];`, `export const isWCustomValueService = (service: string | undefined): service is WCustomValueService =>\n${indent('wCustomValueServiceNames.includes(service as WCustomValueService);')}`);
1084
1090
  };
@@ -1123,14 +1129,19 @@ const generatePropertyDescriptors = (entity, entities, services, options) => [..
1123
1129
  value: value !== undefined ? (typeof value === 'number' ? value : generateString(value)) : undefined
1124
1130
  }))
1125
1131
  }));
1126
- const generateEntityProperties = (entities, aliases, services, options) => {
1132
+ const generateEntityProperties = (entities, services, options) => {
1127
1133
  const typeName = 'WEntityProperties';
1128
1134
  const propertyMap = [
1129
1135
  ...entities.entries(),
1130
- ...[...aliases.entries()].map(([service, type]) => [service, entities.get(camelCase(type))])
1131
- ].map(([entity, data]) => ({
1132
- key: entity,
1133
- value: generatePropertyDescriptors(data, entities, services, options)
1136
+ ...services
1137
+ .filter(({ relatedEntity }) => !!relatedEntity)
1138
+ .filter(({ name }) => !entities.get(name))
1139
+ .map(({ name, relatedEntity }) => {
1140
+ return [name, relatedEntity];
1141
+ })
1142
+ ].map(([entityName, entity]) => ({
1143
+ key: entityName,
1144
+ value: generatePropertyDescriptors(entity, entities, services, options)
1134
1145
  }));
1135
1146
  return generateStatements(`export type ${typeName} = Partial<Record<WEntity, Partial<Record<string, WEntityPropertyMeta>>>>;`, `export const wEntityProperties: ${typeName} = ${generateObject(propertyMap)};`);
1136
1147
  };
@@ -1180,7 +1191,7 @@ const generateGroupedServices = (services) => {
1180
1191
  }), ...typeGuards);
1181
1192
  };
1182
1193
 
1183
- const generateMaps = (enums, entities, services, aliases, options) => {
1194
+ const generateMaps = (enums, entities, services, context, options) => {
1184
1195
  const enumInstances = `export const wEnums = ${generateObject([...enums.keys()].map((v) => ({ key: v, value: v })))};`;
1185
1196
  const entityNames = `export const wEntityNames: WEntity[] = ${generateArray([...entities.keys()])};`;
1186
1197
  const generatedServices = [...services.values()];
@@ -1194,75 +1205,116 @@ const generateMaps = (enums, entities, services, aliases, options) => {
1194
1205
  value: v.serviceFnName,
1195
1206
  comment: v.deprecated ? '@deprecated' : undefined
1196
1207
  })))};`;
1197
- return {
1198
- source: generateStatements(
1199
- /* Enums */
1200
- generateInterface('WEnums', [...enums.keys()].map((name) => ({ name, type: name, required: true }))), generateType('WEnum', 'keyof WEnums'), enumInstances,
1201
- /* Entities */
1202
- generateInterface('WEntities', [
1203
- ...[...entities.keys()].map((name) => ({ name, type: loosePascalCase(name), required: true })),
1204
- ...[...aliases.entries()].map(([name, type]) => ({ name, type, required: true }))
1205
- ].sort((a, b) => (a.name > b.name ? 1 : -1))), generateType('WEntity', 'keyof WEntities'), entityNames,
1206
- /* Services */
1207
- serviceInstances, generateType('WServices', 'typeof wServices'), generateType('WService', 'keyof WServices'), serviceFactories, generateType('WServiceFactories', 'typeof wServiceFactories'),
1208
- /* Service Utils */
1209
- generateGroupedServices(generatedServices), generateCustomValueServices(entities, generatedServices),
1210
- /* Entity Properties (Runtime Meta Infos) */
1211
- generateEntityProperties(entities, aliases, generatedServices, options))
1212
- };
1208
+ return generateStatements(
1209
+ /* Enums */
1210
+ generateInterface('WEnums', [...enums.keys()].map((name) => ({ name, type: name, required: true }))), generateType('WEnum', 'keyof WEnums'), enumInstances,
1211
+ /* Entities */
1212
+ generateInterface('WEntities', [
1213
+ ...[...entities.entries()].map(([name, entity]) => ({
1214
+ name,
1215
+ type: entity.interfaceName,
1216
+ required: true
1217
+ })),
1218
+ ...generatedServices
1219
+ .filter(({ relatedEntity }) => !!relatedEntity)
1220
+ .filter(({ name }) => !entities.get(name))
1221
+ .map(({ name, relatedEntity }) => ({
1222
+ name,
1223
+ type: relatedEntity.interfaceName,
1224
+ required: true
1225
+ }))
1226
+ ].sort((a, b) => (a.name > b.name ? 1 : -1))), generateType('WEntity', 'keyof WEntities'), entityNames,
1227
+ /* Services */
1228
+ serviceInstances, generateType('WServices', 'typeof wServices'), generateType('WService', 'keyof WServices'), serviceFactories, generateType('WServiceFactories', 'typeof wServiceFactories'),
1229
+ /* Service Utils */
1230
+ generateGroupedServices(generatedServices), generateCustomValueServices(generatedServices),
1231
+ /* Entity Properties (Runtime Meta Infos) */
1232
+ generateEntityProperties(entities, generatedServices, options));
1213
1233
  };
1214
1234
 
1215
- const parseReferencedEntityType = (obj) => pascalCase(obj.$ref.replace(/.*\//, ''));
1216
- const extractServiceAliases = (doc, schemas) => {
1217
- const aliases = new Map();
1218
- for (const [path, methods] of Object.entries(doc.paths)) {
1219
- const parsed = parseEndpointPath(path);
1220
- if (!parsed || !methods || parsed.type !== WeclappEndpointType.ROOT || schemas.has(parsed.service)) {
1221
- continue;
1235
+ function extractRelatedEntityName(serviceEndpoints, responses) {
1236
+ const rootEndpoint = serviceEndpoints.find((v) => v.endpoint.type === WeclappEndpointType.ROOT);
1237
+ if (!rootEndpoint)
1238
+ return;
1239
+ const response = rootEndpoint?.path.get?.responses['200'];
1240
+ if (!response)
1241
+ return;
1242
+ let responseObject;
1243
+ if (isReferenceObject(response)) {
1244
+ const refName = getRefName(response);
1245
+ responseObject = responses.get(refName);
1246
+ }
1247
+ else {
1248
+ responseObject = response;
1249
+ }
1250
+ const responseSchema = responseObject?.content?.['application/json'].schema;
1251
+ if (responseSchema) {
1252
+ if (isReferenceObject(responseSchema)) {
1253
+ return;
1222
1254
  }
1223
- const body = methods[OpenAPIV3.HttpMethods.GET]?.responses['200'];
1224
- if (isResponseObject(body) && body.content?.['application/json']) {
1225
- const responseSchema = body.content['application/json'].schema;
1226
- if (!responseSchema || isReferenceObject(responseSchema)) {
1227
- continue;
1228
- }
1229
- const resultSchema = responseSchema.properties?.result;
1230
- if (!resultSchema) {
1231
- continue;
1232
- }
1233
- if (isReferenceObject(resultSchema)) {
1234
- aliases.set(parsed.service, parseReferencedEntityType(resultSchema));
1235
- continue;
1236
- }
1237
- if (isArraySchemaObject(resultSchema)) {
1238
- const resultItemSchema = resultSchema.items;
1239
- if (isReferenceObject(resultItemSchema)) {
1240
- aliases.set(parsed.service, parseReferencedEntityType(resultItemSchema));
1241
- }
1255
+ const resultSchema = responseSchema.properties?.result;
1256
+ if (!resultSchema) {
1257
+ return;
1258
+ }
1259
+ if (isReferenceObject(resultSchema)) {
1260
+ return getRefName(resultSchema);
1261
+ }
1262
+ else if (isArraySchemaObject(resultSchema)) {
1263
+ const resultItemSchema = resultSchema.items;
1264
+ if (isReferenceObject(resultItemSchema)) {
1265
+ return getRefName(resultItemSchema);
1242
1266
  }
1243
1267
  }
1244
1268
  }
1269
+ }
1270
+
1271
+ const extractServiceAliases = (endpoints, responses) => {
1272
+ const aliases = new Map();
1273
+ for (const [serviceName, serviceEndpoints] of endpoints) {
1274
+ const relatedEntityName = extractRelatedEntityName(serviceEndpoints, responses);
1275
+ if (relatedEntityName)
1276
+ aliases.set(serviceName, relatedEntityName);
1277
+ }
1245
1278
  return aliases;
1246
1279
  };
1247
- const extractSchemas = (doc) => {
1280
+ const extractContext = (doc) => {
1281
+ const endpoints = parseEndpointsPaths(doc.paths);
1248
1282
  const schemas = new Map();
1249
1283
  for (const [name, schema] of Object.entries(doc.components?.schemas ?? {})) {
1250
1284
  if (!isReferenceObject(schema)) {
1251
1285
  schemas.set(name, schema);
1252
1286
  }
1253
1287
  }
1254
- const aliases = extractServiceAliases(doc, schemas);
1255
- return { schemas, aliases };
1288
+ const responses = new Map();
1289
+ for (const [name, response] of Object.entries(doc.components?.responses ?? {})) {
1290
+ if (!isReferenceObject(response)) {
1291
+ responses.set(name, response);
1292
+ }
1293
+ }
1294
+ const parameters = new Map();
1295
+ for (const [name, parameter] of Object.entries(doc.components?.parameters ?? {})) {
1296
+ if (!isReferenceObject(parameter)) {
1297
+ parameters.set(name, parameter);
1298
+ }
1299
+ }
1300
+ const requestBodies = new Map();
1301
+ for (const [name, requestBody] of Object.entries(doc.components?.requestBodies ?? {})) {
1302
+ if (!isReferenceObject(requestBody)) {
1303
+ requestBodies.set(name, requestBody);
1304
+ }
1305
+ }
1306
+ const aliases = extractServiceAliases(endpoints, responses);
1307
+ return { endpoints, schemas, responses, parameters, requestBodies, aliases };
1256
1308
  };
1257
1309
 
1258
1310
  const generate = (doc, options) => {
1259
- const { schemas, aliases } = extractSchemas(doc);
1260
- const enums = generateEnums(schemas);
1261
- const entities = generateEntities(schemas, enums);
1262
- const entityFilterProps = generateEntityFilterProps(entities, enums);
1263
- const services = generateServices(doc.paths, entities, aliases, options);
1264
- const maps = generateMaps(enums, entities, services, aliases, options);
1265
- return generateStatements(generateBase(options.target, doc.info.version, options), generateBlockComment('ENUMS', generateStatements(...[...enums.values()].map((v) => v.source))), generateBlockComment('ENTITY FILTER PROPS', generateStatements(...[...entityFilterProps.values()].map((v) => v.source))), generateBlockComment('ENTITIES', generateStatements(...[...entities.values()].map((v) => v.source))), generateBlockComment('SERVICES', generateStatements(...[...services.values()].map((v) => v.source))), generateBlockComment('MAPS', generateStatements(maps.source)));
1311
+ const context = extractContext(doc);
1312
+ const base = generateBase(doc.info.version, options);
1313
+ const enums = generateEnums(context);
1314
+ const entities = generateEntities(context);
1315
+ const services = generateServices(entities, context, options);
1316
+ const maps = generateMaps(enums, entities, services, context, options);
1317
+ return generateStatements(generateBlockComment('BASE', base), generateBlockComment('ENUMS', generateStatements(...[...enums.values()].map((v) => v.source))), generateBlockComment('ENTITIES', generateStatements(...[...entities.values()].map((v) => v.source))), generateBlockComment('FILTERS', generateStatements(...[...entities.values()].map((v) => v.filterSource))), generateBlockComment('SERVICES', generateStatements(...[...services.values()].map((v) => v.source))), generateBlockComment('MAPS', maps));
1266
1318
  };
1267
1319
 
1268
1320
  const hash = (content, algorithm = 'sha256') => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weclapp/sdk",
3
- "version": "2.0.0-dev.50",
3
+ "version": "2.0.0-dev.51",
4
4
  "description": "weclapp SDK Generator",
5
5
  "author": "weclapp",
6
6
  "sideEffects": false,
@@ -39,10 +39,14 @@
39
39
  "scripts": {
40
40
  "build": "rollup --config rollup.config.ts --configPlugin typescript={tsconfig:\\'tsconfig.node.json\\'} --configImportAttributesKey with",
41
41
  "build:watch": "npm run build -- --watch",
42
- "cli:browser": "./bin/cli.js test/openapi.json --target browser",
43
- "cli:browser:cache": "./bin/cli.js test/openapi.json --target browser --cache",
44
- "cli:browser.rx": "./bin/cli.js test/openapi.json --target browser.rx",
45
- "cli:browser.rx:cache": "./bin/cli.js test/openapi.json --target browser.rx --cache",
42
+ "cli:browser:v1": "./bin/cli.js test/openapi.json --target browser",
43
+ "cli:browser.rx:v1": "./bin/cli.js test/openapi.json --target browser.rx",
44
+ "cli:browser:v2": "./bin/cli.js test/openapi_v2.json --target browser",
45
+ "cli:browser.rx:v2": "./bin/cli.js test/openapi_v2.json --target browser.rx",
46
+ "cli:browser:v3": "./bin/cli.js test/openapi_v3.json --target browser",
47
+ "cli:browser:v3:cache": "./bin/cli.js test/openapi_v3.json --target browser --cache",
48
+ "cli:browser.rx:v3": "./bin/cli.js test/openapi_v3.json --target browser.rx",
49
+ "cli:browser.rx:v3:cache": "./bin/cli.js test/openapi_v3.json --target browser.rx --cache",
46
50
  "cli:node": "./bin/cli.js test/openapi.json --target node",
47
51
  "cli:node:cache": "./bin/cli.js test/openapi.json --target node --cache",
48
52
  "cli:node.rx": "./bin/cli.js test/openapi.json --target node.rx",
@@ -51,7 +55,7 @@
51
55
  "prettier:fix": "prettier . --write",
52
56
  "lint": "eslint ./src --cache",
53
57
  "lint:fix": "npm run lint -- --fix",
54
- "ci": "npm run prettier && npm run lint && npm run build && npm run cli:browser",
58
+ "ci": "npm run prettier && npm run lint && npm run build && npm run cli:browser:v1 && npm run cli:browser:v2 && npm run cli:browser:v3",
55
59
  "release": "standard-version"
56
60
  },
57
61
  "devDependencies": {