@5minds/node-red-contrib-processcube-tools 1.1.0-feature-975857-mg4vqpai → 1.2.0-develop-2eb127-mg68t7xt

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.
@@ -1,277 +0,0 @@
1
- /**
2
- * Copyright 2015, 2016 IBM Corp.
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- **/
16
-
17
- const DEFAULT_TEMPLATE = {
18
- openapi: '3.0.0',
19
- info: {
20
- title: 'My Node-RED API',
21
- version: '1.0.0',
22
- description: 'A sample API',
23
- },
24
- servers: [
25
- {
26
- url: 'http://localhost:1880/',
27
- description: 'Local server',
28
- },
29
- ],
30
- paths: {},
31
- components: {
32
- schemas: {},
33
- responses: {},
34
- parameters: {},
35
- securitySchemes: {},
36
- },
37
- tags: [],
38
- };
39
-
40
- module.exports = function (RED) {
41
- 'use strict';
42
-
43
- const path = require('path');
44
-
45
- const convToSwaggerPath = (x) => `/{${x.substring(2)}}`;
46
- const trimAll = (ary) => ary.map((x) => x.trim());
47
- const csvStrToArray = (csvStr) => (csvStr ? trimAll(csvStr.split(',')) : []);
48
- const ensureLeadingSlash = (url) => (url.startsWith('/') ? url : '/' + url);
49
- const stripTerminalSlash = (url) => (url.length > 1 && url.endsWith('/') ? url.slice(0, -1) : url);
50
- const regexColons = /\/:\w*/g;
51
-
52
- // Helper function to convert collection format to OpenAPI 3.0 style
53
- const getStyleFromCollectionFormat = (collectionFormat) => {
54
- const formatMap = {
55
- csv: 'simple',
56
- ssv: 'spaceDelimited',
57
- tsv: 'pipeDelimited',
58
- pipes: 'pipeDelimited',
59
- multi: 'form',
60
- };
61
- return formatMap[collectionFormat] || 'simple';
62
- };
63
-
64
- RED.httpNode.get('/http-api/swagger.json', (req, res) => {
65
- try {
66
- const { httpNodeRoot, openapi: { template = {}, parameters: additionalParams = [] } = {} } = RED.settings;
67
-
68
- const resp = { ...DEFAULT_TEMPLATE, ...template };
69
- resp.paths = {};
70
-
71
- // Update server URL to include the httpNodeRoot
72
- if (httpNodeRoot && httpNodeRoot !== '/') {
73
- resp.servers = resp.servers.map((server) => ({
74
- ...server,
75
- url: server.url.replace(/\/$/, '') + httpNodeRoot,
76
- }));
77
- }
78
-
79
- RED.nodes.eachNode((node) => {
80
- const { name, type, method, swaggerDoc, url } = node;
81
-
82
- if (type === 'http in' && swaggerDoc) {
83
- const swaggerDocNode = RED.nodes.getNode(swaggerDoc);
84
-
85
- if (swaggerDocNode) {
86
- // Convert Node-RED path parameters to OpenAPI format
87
- const endPoint = stripTerminalSlash(
88
- ensureLeadingSlash(url.replace(regexColons, convToSwaggerPath)),
89
- );
90
-
91
- if (!resp.paths[endPoint]) resp.paths[endPoint] = {};
92
-
93
- const {
94
- summary = name || `${method.toUpperCase()} ${endPoint}`,
95
- description = '',
96
- tags = '',
97
- deprecated = false,
98
- parameters = [],
99
- requestBody = null,
100
- responses = {},
101
- } = swaggerDocNode;
102
-
103
- const aryTags = csvStrToArray(tags);
104
-
105
- const operation = {
106
- summary,
107
- description,
108
- tags: aryTags,
109
- deprecated,
110
- parameters: [...parameters, ...additionalParams].map((param) => {
111
- const paramDef = {
112
- name: param.name,
113
- in: param.in,
114
- required: param.required || false,
115
- description: param.description || '',
116
- };
117
-
118
- // Handle parameter schema - preserve the original schema structure
119
- if (param.schema) {
120
- // If there's already a schema object, use it
121
- paramDef.schema = param.schema;
122
- } else if (param.type) {
123
- // Build schema from individual type properties
124
- paramDef.schema = { type: param.type };
125
- if (param.format) {
126
- paramDef.schema.format = param.format;
127
- }
128
- if (param.type === 'array' && param.items) {
129
- paramDef.schema.items = param.items;
130
- }
131
- if (param.collectionFormat && param.type === 'array') {
132
- paramDef.style = getStyleFromCollectionFormat(param.collectionFormat);
133
- paramDef.explode = param.collectionFormat === 'multi';
134
- }
135
- }
136
-
137
- return paramDef;
138
- }),
139
- responses: {},
140
- };
141
-
142
- // Add request body if it exists
143
- if (requestBody && Object.keys(requestBody.content || {}).length > 0) {
144
- const content = requestBody.content;
145
- Object.keys(content).forEach(contentType => {
146
- if (contentType.includes('xml') && content[contentType].example) {
147
- content['text/plain'] = {
148
- schema: { type: 'string' },
149
- example: content[contentType].example.replace(/\\n/g, '\n')
150
- };
151
- delete content[contentType];
152
- }
153
- });
154
- operation.requestBody = requestBody;
155
- }
156
-
157
- // Process responses
158
- if (responses && typeof responses === 'object') {
159
- Object.keys(responses).forEach((status) => {
160
- const responseDetails = responses[status];
161
- operation.responses[status] = {
162
- description: responseDetails.description || 'No description',
163
- };
164
-
165
- // Add content if schema exists
166
- if (responseDetails.schema) {
167
- operation.responses[status].content = {
168
- 'application/json': {
169
- schema: responseDetails.schema,
170
- },
171
- };
172
- }
173
- });
174
- }
175
-
176
- // Ensure at least one response exists
177
- if (Object.keys(operation.responses).length === 0) {
178
- operation.responses['200'] = {
179
- description: 'Successful response',
180
- };
181
- }
182
-
183
- resp.paths[endPoint][method.toLowerCase()] = operation;
184
- }
185
- }
186
- });
187
-
188
- // Clean up empty sections
189
- cleanupOpenAPISpec(resp);
190
- res.json(resp);
191
- } catch (error) {
192
- console.error('Error generating Swagger JSON:', error);
193
- res.status(500).json({ error: 'Internal server error' });
194
- }
195
- });
196
-
197
- function cleanupOpenAPISpec(spec) {
198
- // Clean up components
199
- if (spec.components) {
200
- ['schemas', 'responses', 'parameters', 'securitySchemes'].forEach((key) => {
201
- if (spec.components[key] && Object.keys(spec.components[key]).length === 0) {
202
- delete spec.components[key];
203
- }
204
- });
205
-
206
- // If all components are empty, remove the components object itself
207
- if (Object.keys(spec.components).length === 0) {
208
- delete spec.components;
209
- }
210
- }
211
-
212
- // Clean up empty tags array
213
- if (Array.isArray(spec.tags) && spec.tags.length === 0) {
214
- delete spec.tags;
215
- }
216
- }
217
-
218
- function SwaggerDoc(n) {
219
- RED.nodes.createNode(this, n);
220
- this.summary = n.summary;
221
- this.description = n.description;
222
- this.tags = n.tags;
223
- this.parameters = n.parameters || [];
224
- this.responses = n.responses || {};
225
- this.requestBody = n.requestBody || null;
226
- this.deprecated = n.deprecated || false;
227
- }
228
- RED.nodes.registerType('swagger-doc', SwaggerDoc);
229
-
230
- // Serve the main Swagger UI HTML file
231
- RED.httpAdmin.get('/swagger-ui/swagger-ui.html', (req, res) => {
232
- const filename = path.join(__dirname, 'swagger-ui', 'swagger-ui.html');
233
- sendFile(res, filename);
234
- });
235
-
236
- // Serve Swagger UI assets
237
- RED.httpAdmin.get('/swagger-ui/*', (req, res, next) => {
238
- let filename = req.params[0];
239
-
240
- // Skip if it's the HTML file (handled by specific route above)
241
- if (filename === 'swagger-ui.html') {
242
- return next();
243
- }
244
-
245
- try {
246
- const swaggerUiPath = require('swagger-ui-dist').getAbsoluteFSPath();
247
- const filePath = path.join(swaggerUiPath, filename);
248
- sendFile(res, filePath);
249
- } catch (err) {
250
- console.error('Error serving Swagger UI asset:', err);
251
- res.status(404).send('File not found');
252
- }
253
- });
254
-
255
- // Serve localization files
256
- RED.httpAdmin.get('/swagger-ui/nls/*', (req, res) => {
257
- const filename = path.join(__dirname, 'locales', req.params[0]);
258
- sendFile(res, filename);
259
- });
260
-
261
- // Generic function to send files
262
- function sendFile(res, filePath) {
263
- const fs = require('fs');
264
-
265
- // Check if file exists
266
- if (!fs.existsSync(filePath)) {
267
- return res.status(404).send('File not found');
268
- }
269
-
270
- res.sendFile(path.resolve(filePath), (err) => {
271
- if (err) {
272
- console.error('Error sending file:', err);
273
- res.status(err.status || 500).send('Error sending file');
274
- }
275
- });
276
- }
277
- };