1 | "use strict";
|
2 | var __importDefault = (this && this.__importDefault) || function (mod) {
|
3 | return (mod && mod.__esModule) ? mod : { "default": mod };
|
4 | };
|
5 | Object.defineProperty(exports, "__esModule", { value: true });
|
6 | const path_1 = __importDefault(require("path"));
|
7 | const typescript_1 = __importDefault(require("typescript"));
|
8 | const openapi_template_1 = require("./openapi-template");
|
9 | const convert_type_1 = require("./convert-type");
|
10 | exports.INTENT_ACTION = 'action';
|
11 |
|
12 |
|
13 |
|
14 | function findActionMethod(node) {
|
15 | const action = node.members.find((node) => {
|
16 | if (node.name) {
|
17 | return node.name.getText() === exports.INTENT_ACTION;
|
18 | }
|
19 | return false;
|
20 | });
|
21 | if (typescript_1.default.isPropertyDeclaration(action)) {
|
22 | return action.initializer;
|
23 | }
|
24 | return action;
|
25 | }
|
26 | function serializeBody(method, checker) {
|
27 | const symbol = checker.getSymbolAtLocation(method.name);
|
28 | let data;
|
29 | let error;
|
30 | if (symbol) {
|
31 | const returnTypeNode = method.type;
|
32 | let dataIndex = 0;
|
33 | let errorIndex = 1;
|
34 | const typeName = checker.typeToString(checker.getTypeFromTypeNode(returnTypeNode));
|
35 | if (typeName.startsWith('Promise<TSaveStatePayload')) {
|
36 | dataIndex = 1;
|
37 | errorIndex = 2;
|
38 | }
|
39 | if (returnTypeNode && typescript_1.default.isTypeReferenceNode(returnTypeNode)) {
|
40 | const typeData = checker.getTypeFromTypeNode(returnTypeNode.typeArguments[dataIndex]);
|
41 | data = convert_type_1.convertType(typeData, checker);
|
42 | const errorArgument = returnTypeNode.typeArguments[errorIndex];
|
43 | if (errorArgument) {
|
44 | const typeError = checker.getTypeFromTypeNode(errorArgument);
|
45 | error = convert_type_1.convertType(typeError, checker);
|
46 | }
|
47 | }
|
48 | }
|
49 | return { data, error };
|
50 | }
|
51 | function resolveParameterTypeIndex(t, checker, i, j) {
|
52 | let index = i;
|
53 | if (checker.typeToString(t).startsWith('TSaveActionEvent')) {
|
54 | index = j;
|
55 | }
|
56 | return index;
|
57 | }
|
58 |
|
59 |
|
60 |
|
61 | function serializeParameters(sym, node, checker) {
|
62 | if (sym) {
|
63 | const typ = checker.getTypeOfSymbolAtLocation(sym, node);
|
64 | if (typ.aliasTypeArguments) {
|
65 | const index = resolveParameterTypeIndex(typ, checker, 0, 1);
|
66 | return convert_type_1.convertType(typ.aliasTypeArguments[index], checker);
|
67 | }
|
68 | }
|
69 | }
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 | function getIntentAuthType(sym, node, checker) {
|
77 | if (sym) {
|
78 | const typ = checker.getTypeOfSymbolAtLocation(sym, node);
|
79 | if (typ.aliasTypeArguments) {
|
80 | const index = resolveParameterTypeIndex(typ, checker, 1, 2);
|
81 | const authType = checker.typeToString(typ.aliasTypeArguments[index]);
|
82 | if (/apiKey/.test(authType))
|
83 | return 'APIKEY';
|
84 | if (/username/.test(authType) && /password/.test(authType))
|
85 | return 'BASIC';
|
86 | if (/accessToken/.test(authType))
|
87 | return 'OAUTH2';
|
88 | if (/undefined/.test(authType))
|
89 | return 'NONE';
|
90 | return authType;
|
91 | }
|
92 | }
|
93 | return;
|
94 | }
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 | function intentTypesToSchemaConverter(intentPath, options = { target: typescript_1.default.ScriptTarget.ES5, module: typescript_1.default.ModuleKind.CommonJS }) {
|
103 | const program = typescript_1.default.createProgram([intentPath], options);
|
104 | const sourceFile = program.getSourceFile(intentPath);
|
105 | const checker = program.getTypeChecker();
|
106 | const output = {
|
107 | requestBody: {},
|
108 | response: {}
|
109 | };
|
110 | if (sourceFile) {
|
111 | typescript_1.default.forEachChild(sourceFile, visit);
|
112 | }
|
113 | return output;
|
114 | function visit(node) {
|
115 |
|
116 | if (!isNodeExported(node)) {
|
117 | return;
|
118 | }
|
119 | if (typescript_1.default.isClassDeclaration(node) && node.name) {
|
120 | const actionMethodNode = findActionMethod(node);
|
121 | if (actionMethodNode) {
|
122 | const parameterNode = actionMethodNode.parameters[0];
|
123 | const sym = checker.getSymbolAtLocation(parameterNode.name);
|
124 | output.requestBody = serializeParameters(sym, parameterNode, checker);
|
125 | output.response = serializeBody(actionMethodNode, checker);
|
126 | output.intentAuthType = getIntentAuthType(sym, parameterNode, checker);
|
127 | }
|
128 | }
|
129 | }
|
130 | function isNodeExported(node) {
|
131 | return ((typescript_1.default.getCombinedModifierFlags(node) & typescript_1.default.ModifierFlags.Export) !== 0 ||
|
132 | (!!node.parent && node.parent.kind === typescript_1.default.SyntaxKind.SourceFile));
|
133 | }
|
134 | }
|
135 | exports.intentTypesToSchemaConverter = intentTypesToSchemaConverter;
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 | function generator({ intentsDir, intents, integrationUuid, integrationName }) {
|
144 | const doc = openapi_template_1.topOfSpec(integrationName);
|
145 | const schemas = intents.sort().reduce((acc, intent) => {
|
146 | const intentPath = path_1.default.join(intentsDir, `${intent}.ts`);
|
147 | const typeSchema = intentTypesToSchemaConverter(intentPath);
|
148 | return Object.assign(acc, openapi_template_1.specPath({
|
149 | integrationUuid,
|
150 | intentName: intent,
|
151 | response: { type: 'object', properties: typeSchema.response },
|
152 | requestBody: typeSchema.requestBody,
|
153 | oauth2: typeSchema.intentAuthType === 'OAUTH2'
|
154 | }));
|
155 | }, {});
|
156 | return JSON.stringify(Object.assign(doc, { paths: schemas }), null, 2);
|
157 | }
|
158 | exports.default = generator;
|