@avtechno/sfr 2.1.0 → 2.1.1
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.
- package/dist/index.mjs +12 -2
- package/dist/sfr-pipeline.mjs +91 -2
- package/dist/types/sfr-pipeline.d.mts +9 -0
- package/package.json +1 -1
- package/src/index.mts +13 -2
- package/src/sfr-pipeline.mts +106 -3
package/dist/index.mjs
CHANGED
|
@@ -58,16 +58,26 @@ async function write_combined_openapi_index(cfg, documents) {
|
|
|
58
58
|
const combined = {
|
|
59
59
|
openapi: "3.1.0",
|
|
60
60
|
info: {},
|
|
61
|
-
paths: {}
|
|
61
|
+
paths: {},
|
|
62
|
+
components: {
|
|
63
|
+
schemas: {}
|
|
64
|
+
}
|
|
62
65
|
};
|
|
63
66
|
for (const [protocol, documentation] of Object.entries(documents)) {
|
|
64
67
|
switch (protocol) {
|
|
65
68
|
case "REST":
|
|
66
69
|
{
|
|
67
70
|
const rest_doc = documentation;
|
|
68
|
-
// Merge REST OpenAPI info and
|
|
71
|
+
// Merge REST OpenAPI info, paths, and components into combined doc
|
|
69
72
|
combined.info = rest_doc.info;
|
|
70
73
|
combined.paths = rest_doc.paths;
|
|
74
|
+
// Merge component schemas if they exist
|
|
75
|
+
if (rest_doc.components?.schemas) {
|
|
76
|
+
combined.components.schemas = {
|
|
77
|
+
...combined.components.schemas,
|
|
78
|
+
...rest_doc.components.schemas
|
|
79
|
+
};
|
|
80
|
+
}
|
|
71
81
|
}
|
|
72
82
|
break;
|
|
73
83
|
}
|
package/dist/sfr-pipeline.mjs
CHANGED
|
@@ -509,7 +509,10 @@ export class SFRPipeline {
|
|
|
509
509
|
const spec = {
|
|
510
510
|
openapi: "3.0.0",
|
|
511
511
|
info: Object.assign({}, this.oas_cfg),
|
|
512
|
-
paths: {}
|
|
512
|
+
paths: {},
|
|
513
|
+
components: {
|
|
514
|
+
schemas: {}
|
|
515
|
+
}
|
|
513
516
|
};
|
|
514
517
|
// Iterate through each protocol (e.g., REST, WS, MQ)
|
|
515
518
|
Object.entries(declaration).forEach(([namespace, module]) => {
|
|
@@ -623,9 +626,30 @@ export class SFRPipeline {
|
|
|
623
626
|
document.description = (document.description || "") + rate_limit_note;
|
|
624
627
|
}
|
|
625
628
|
}
|
|
629
|
+
// Generate schema name from namespace and endpoint
|
|
630
|
+
const schema_name = this.generate_schema_name(namespace, endpoint, method);
|
|
626
631
|
//Insert either a parameter or a requestBody according to the method type
|
|
627
632
|
if (validator_type === "joi") {
|
|
628
|
-
|
|
633
|
+
const swagger_schema = j2s(validator).swagger;
|
|
634
|
+
if (method === "get") {
|
|
635
|
+
// For GET requests, convert schema properties to query parameters
|
|
636
|
+
document.parameters = this.convert_schema_to_query_parameters(swagger_schema, schema_name, spec);
|
|
637
|
+
}
|
|
638
|
+
else {
|
|
639
|
+
// For POST/PUT/PATCH/DELETE, use requestBody with application/json
|
|
640
|
+
// Store schema in components
|
|
641
|
+
spec.components.schemas[schema_name] = swagger_schema;
|
|
642
|
+
document.requestBody = {
|
|
643
|
+
required: true,
|
|
644
|
+
content: {
|
|
645
|
+
"application/json": {
|
|
646
|
+
schema: {
|
|
647
|
+
$ref: `#/components/schemas/${schema_name}`
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
}
|
|
629
653
|
}
|
|
630
654
|
// Convert Multer validators to multipart/form-data OpenAPI schema
|
|
631
655
|
if (validator_type === "multer") {
|
|
@@ -644,6 +668,71 @@ export class SFRPipeline {
|
|
|
644
668
|
spec.info.meta = Object.fromEntries(Object.entries(spec.info.meta).map(([k, v]) => [`x-${k}`, v]));
|
|
645
669
|
return spec;
|
|
646
670
|
}
|
|
671
|
+
/**
|
|
672
|
+
* Generates a unique schema name from namespace, endpoint, and method.
|
|
673
|
+
*/
|
|
674
|
+
generate_schema_name(namespace, endpoint, method) {
|
|
675
|
+
// Convert to PascalCase and create a descriptive schema name
|
|
676
|
+
const pascal_case = (str) => str
|
|
677
|
+
.split(/[-_]/)
|
|
678
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
679
|
+
.join('');
|
|
680
|
+
const method_prefix = method.charAt(0).toUpperCase() + method.slice(1).toLowerCase();
|
|
681
|
+
const namespace_part = pascal_case(namespace);
|
|
682
|
+
const endpoint_part = pascal_case(endpoint);
|
|
683
|
+
return `${method_prefix}${namespace_part}${endpoint_part}Request`;
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Converts a JSON schema to OpenAPI query parameters array.
|
|
687
|
+
* Stores complex schemas in components and references them.
|
|
688
|
+
*/
|
|
689
|
+
convert_schema_to_query_parameters(schema, base_name, spec) {
|
|
690
|
+
const parameters = [];
|
|
691
|
+
if (!schema || !schema.properties) {
|
|
692
|
+
return parameters;
|
|
693
|
+
}
|
|
694
|
+
const required_fields = schema.required || [];
|
|
695
|
+
for (const [prop_name, prop_schema] of Object.entries(schema.properties)) {
|
|
696
|
+
const param_schema = prop_schema;
|
|
697
|
+
const is_required = required_fields.includes(prop_name);
|
|
698
|
+
// Check if the property is a complex type (object or array of objects)
|
|
699
|
+
const is_complex = param_schema.type === 'object' ||
|
|
700
|
+
(param_schema.type === 'array' && param_schema.items?.type === 'object');
|
|
701
|
+
if (is_complex) {
|
|
702
|
+
// Store complex schemas in components and reference them
|
|
703
|
+
const component_name = `${base_name}${prop_name.charAt(0).toUpperCase() + prop_name.slice(1)}`;
|
|
704
|
+
spec.components.schemas[component_name] = param_schema;
|
|
705
|
+
parameters.push({
|
|
706
|
+
name: prop_name,
|
|
707
|
+
in: "query",
|
|
708
|
+
required: is_required,
|
|
709
|
+
description: param_schema.description || "",
|
|
710
|
+
content: {
|
|
711
|
+
"application/json": {
|
|
712
|
+
schema: {
|
|
713
|
+
$ref: `#/components/schemas/${component_name}`
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
else {
|
|
720
|
+
// Simple types can be defined inline
|
|
721
|
+
const param = {
|
|
722
|
+
name: prop_name,
|
|
723
|
+
in: "query",
|
|
724
|
+
required: is_required,
|
|
725
|
+
schema: { ...param_schema }
|
|
726
|
+
};
|
|
727
|
+
if (param_schema.description) {
|
|
728
|
+
param.description = param_schema.description;
|
|
729
|
+
delete param.schema.description;
|
|
730
|
+
}
|
|
731
|
+
parameters.push(param);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
return parameters;
|
|
735
|
+
}
|
|
647
736
|
// Method to generate an AsyncAPI document from a WSNamespaceDeclaration
|
|
648
737
|
generate_ws_async_api_document(declaration) {
|
|
649
738
|
const spec = {
|
|
@@ -68,6 +68,15 @@ export declare class SFRPipeline {
|
|
|
68
68
|
private bind_mq_fns;
|
|
69
69
|
private spec_generation;
|
|
70
70
|
private generate_open_api_document;
|
|
71
|
+
/**
|
|
72
|
+
* Generates a unique schema name from namespace, endpoint, and method.
|
|
73
|
+
*/
|
|
74
|
+
private generate_schema_name;
|
|
75
|
+
/**
|
|
76
|
+
* Converts a JSON schema to OpenAPI query parameters array.
|
|
77
|
+
* Stores complex schemas in components and references them.
|
|
78
|
+
*/
|
|
79
|
+
private convert_schema_to_query_parameters;
|
|
71
80
|
private generate_ws_async_api_document;
|
|
72
81
|
private generate_mq_async_api_document;
|
|
73
82
|
private print_to_console;
|
package/package.json
CHANGED
package/src/index.mts
CHANGED
|
@@ -68,16 +68,27 @@ async function write_combined_openapi_index(cfg: ParserCFG, documents: ServiceDo
|
|
|
68
68
|
const combined: any = {
|
|
69
69
|
openapi: "3.1.0",
|
|
70
70
|
info: {},
|
|
71
|
-
paths: {}
|
|
71
|
+
paths: {},
|
|
72
|
+
components: {
|
|
73
|
+
schemas: {}
|
|
74
|
+
}
|
|
72
75
|
};
|
|
73
76
|
|
|
74
77
|
for (const [protocol, documentation] of Object.entries(documents)) {
|
|
75
78
|
switch (protocol) {
|
|
76
79
|
case "REST": {
|
|
77
80
|
const rest_doc = documentation as OAPI_Document;
|
|
78
|
-
// Merge REST OpenAPI info and
|
|
81
|
+
// Merge REST OpenAPI info, paths, and components into combined doc
|
|
79
82
|
combined.info = rest_doc.info;
|
|
80
83
|
combined.paths = rest_doc.paths;
|
|
84
|
+
|
|
85
|
+
// Merge component schemas if they exist
|
|
86
|
+
if (rest_doc.components?.schemas) {
|
|
87
|
+
combined.components.schemas = {
|
|
88
|
+
...combined.components.schemas,
|
|
89
|
+
...rest_doc.components.schemas
|
|
90
|
+
};
|
|
91
|
+
}
|
|
81
92
|
} break;
|
|
82
93
|
}
|
|
83
94
|
}
|
package/src/sfr-pipeline.mts
CHANGED
|
@@ -562,7 +562,10 @@ export class SFRPipeline {
|
|
|
562
562
|
const spec: OAPI_Document = {
|
|
563
563
|
openapi: "3.0.0",
|
|
564
564
|
info : Object.assign({}, this.oas_cfg),
|
|
565
|
-
paths: {}
|
|
565
|
+
paths: {},
|
|
566
|
+
components: {
|
|
567
|
+
schemas: {}
|
|
568
|
+
}
|
|
566
569
|
};
|
|
567
570
|
|
|
568
571
|
// Iterate through each protocol (e.g., REST, WS, MQ)
|
|
@@ -686,9 +689,32 @@ export class SFRPipeline {
|
|
|
686
689
|
}
|
|
687
690
|
}
|
|
688
691
|
|
|
692
|
+
// Generate schema name from namespace and endpoint
|
|
693
|
+
const schema_name = this.generate_schema_name(namespace, endpoint, method);
|
|
694
|
+
|
|
689
695
|
//Insert either a parameter or a requestBody according to the method type
|
|
690
|
-
if(validator_type === "joi"){
|
|
691
|
-
|
|
696
|
+
if (validator_type === "joi") {
|
|
697
|
+
const swagger_schema = j2s(validator).swagger;
|
|
698
|
+
|
|
699
|
+
if (method === "get") {
|
|
700
|
+
// For GET requests, convert schema properties to query parameters
|
|
701
|
+
document.parameters = this.convert_schema_to_query_parameters(swagger_schema, schema_name, spec);
|
|
702
|
+
} else {
|
|
703
|
+
// For POST/PUT/PATCH/DELETE, use requestBody with application/json
|
|
704
|
+
// Store schema in components
|
|
705
|
+
spec.components.schemas[schema_name] = swagger_schema;
|
|
706
|
+
|
|
707
|
+
document.requestBody = {
|
|
708
|
+
required: true,
|
|
709
|
+
content: {
|
|
710
|
+
"application/json": {
|
|
711
|
+
schema: {
|
|
712
|
+
$ref: `#/components/schemas/${schema_name}`
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
}
|
|
692
718
|
}
|
|
693
719
|
|
|
694
720
|
// Convert Multer validators to multipart/form-data OpenAPI schema
|
|
@@ -710,6 +736,83 @@ export class SFRPipeline {
|
|
|
710
736
|
return spec;
|
|
711
737
|
}
|
|
712
738
|
|
|
739
|
+
/**
|
|
740
|
+
* Generates a unique schema name from namespace, endpoint, and method.
|
|
741
|
+
*/
|
|
742
|
+
private generate_schema_name(namespace: string, endpoint: string, method: string): string {
|
|
743
|
+
// Convert to PascalCase and create a descriptive schema name
|
|
744
|
+
const pascal_case = (str: string) => str
|
|
745
|
+
.split(/[-_]/)
|
|
746
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
747
|
+
.join('');
|
|
748
|
+
|
|
749
|
+
const method_prefix = method.charAt(0).toUpperCase() + method.slice(1).toLowerCase();
|
|
750
|
+
const namespace_part = pascal_case(namespace);
|
|
751
|
+
const endpoint_part = pascal_case(endpoint);
|
|
752
|
+
|
|
753
|
+
return `${method_prefix}${namespace_part}${endpoint_part}Request`;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Converts a JSON schema to OpenAPI query parameters array.
|
|
758
|
+
* Stores complex schemas in components and references them.
|
|
759
|
+
*/
|
|
760
|
+
private convert_schema_to_query_parameters(schema: any, base_name: string, spec: OAPI_Document): any[] {
|
|
761
|
+
const parameters: any[] = [];
|
|
762
|
+
|
|
763
|
+
if (!schema || !schema.properties) {
|
|
764
|
+
return parameters;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
const required_fields = schema.required || [];
|
|
768
|
+
|
|
769
|
+
for (const [prop_name, prop_schema] of Object.entries(schema.properties)) {
|
|
770
|
+
const param_schema: any = prop_schema;
|
|
771
|
+
const is_required = required_fields.includes(prop_name);
|
|
772
|
+
|
|
773
|
+
// Check if the property is a complex type (object or array of objects)
|
|
774
|
+
const is_complex = param_schema.type === 'object' ||
|
|
775
|
+
(param_schema.type === 'array' && param_schema.items?.type === 'object');
|
|
776
|
+
|
|
777
|
+
if (is_complex) {
|
|
778
|
+
// Store complex schemas in components and reference them
|
|
779
|
+
const component_name = `${base_name}${prop_name.charAt(0).toUpperCase() + prop_name.slice(1)}`;
|
|
780
|
+
spec.components.schemas[component_name] = param_schema;
|
|
781
|
+
|
|
782
|
+
parameters.push({
|
|
783
|
+
name: prop_name,
|
|
784
|
+
in: "query",
|
|
785
|
+
required: is_required,
|
|
786
|
+
description: param_schema.description || "",
|
|
787
|
+
content: {
|
|
788
|
+
"application/json": {
|
|
789
|
+
schema: {
|
|
790
|
+
$ref: `#/components/schemas/${component_name}`
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
} else {
|
|
796
|
+
// Simple types can be defined inline
|
|
797
|
+
const param: any = {
|
|
798
|
+
name: prop_name,
|
|
799
|
+
in: "query",
|
|
800
|
+
required: is_required,
|
|
801
|
+
schema: { ...param_schema }
|
|
802
|
+
};
|
|
803
|
+
|
|
804
|
+
if (param_schema.description) {
|
|
805
|
+
param.description = param_schema.description;
|
|
806
|
+
delete param.schema.description;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
parameters.push(param);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
return parameters;
|
|
814
|
+
}
|
|
815
|
+
|
|
713
816
|
// Method to generate an AsyncAPI document from a WSNamespaceDeclaration
|
|
714
817
|
private generate_ws_async_api_document(declaration: WSNamespaceDeclaration): AsyncAPIDocument {
|
|
715
818
|
const spec: any = {
|