@tremho/mist-lift 2.2.8 → 2.4.0
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/README.md +9 -1
- package/build/commands/actions/updateDeployedPermissions.js +109 -0
- package/build/commands/actions/updateDeployedPermissions.js.map +1 -0
- package/build/commands/builtin/ApiDocMaker.js +18 -10
- package/build/commands/builtin/ApiDocMaker.js.map +1 -1
- package/build/commands/builtin/BuiltInHandler.js +6 -3
- package/build/commands/builtin/BuiltInHandler.js.map +1 -1
- package/build/commands/builtin/ExportWebroot.js +242 -0
- package/build/commands/builtin/ExportWebroot.js.map +1 -0
- package/build/commands/builtin/StageWebrootZip.js +10 -6
- package/build/commands/builtin/StageWebrootZip.js.map +1 -1
- package/build/commands/builtin/prebuilt-zips/API.zip +0 -0
- package/build/commands/builtin/prebuilt-zips/FileServe.zip +0 -0
- package/build/commands/builtin/prebuilt-zips/Webroot.zip +0 -0
- package/build/commands/builtin/webroot-export/s3webroot.js +117 -0
- package/build/commands/builtin/webroot-export/s3webroot.js.map +1 -0
- package/build/commands/deploy.js +6 -4
- package/build/commands/deploy.js.map +1 -1
- package/build/commands/package.js +31 -1
- package/build/commands/package.js.map +1 -1
- package/build/commands/publish.js +40 -13
- package/build/commands/publish.js.map +1 -1
- package/build/commands/start.js +2 -1
- package/build/commands/start.js.map +1 -1
- package/build/commands/update.js +1 -0
- package/build/commands/update.js.map +1 -1
- package/build/expressRoutes/all.js +1 -0
- package/build/expressRoutes/all.js.map +1 -1
- package/build/expressRoutes/functionBinder.js +159 -17
- package/build/expressRoutes/functionBinder.js.map +1 -1
- package/build/lib/DirectoryUtils.js +2 -1
- package/build/lib/DirectoryUtils.js.map +1 -1
- package/build/lib/IdSrc.js +29 -5
- package/build/lib/IdSrc.js.map +1 -1
- package/build/lib/TypeCheck.js +1204 -0
- package/build/lib/TypeCheck.js.map +1 -0
- package/build/lib/executeCommand.js +1 -1
- package/build/lib/executeCommand.js.map +1 -1
- package/build/lib/openAPI/openApiConstruction.js +238 -54
- package/build/lib/openAPI/openApiConstruction.js.map +1 -1
- package/build/lift.js +1 -1
- package/build/lift.js.map +1 -1
- package/package.json +5 -2
- package/src/commands/actions/updateDeployedPermissions.ts +80 -0
- package/src/commands/builtin/ApiDocMaker.ts +17 -10
- package/src/commands/builtin/BuiltInHandler.ts +7 -2
- package/src/commands/builtin/ExportWebroot.ts +195 -0
- package/src/commands/builtin/StageWebrootZip.ts +13 -5
- package/src/commands/builtin/prebuilt-zips/API.zip +0 -0
- package/src/commands/builtin/prebuilt-zips/FileServe.zip +0 -0
- package/src/commands/builtin/prebuilt-zips/Webroot.zip +0 -0
- package/src/commands/builtin/webroot-export/s3webroot.ts +78 -0
- package/src/commands/deploy.ts +6 -4
- package/src/commands/package.ts +33 -2
- package/src/commands/publish.ts +37 -12
- package/src/commands/start.ts +2 -1
- package/src/commands/update.ts +1 -0
- package/src/expressRoutes/all.ts +1 -0
- package/src/expressRoutes/functionBinder.ts +152 -16
- package/src/lib/DirectoryUtils.ts +2 -1
- package/src/lib/IdSrc.ts +17 -4
- package/src/lib/TypeCheck.ts +1168 -0
- package/src/lib/executeCommand.ts +1 -1
- package/src/lib/openAPI/openApiConstruction.ts +225 -41
- package/src/lift.ts +1 -1
- package/templateData/function-main-ts +8 -1
|
@@ -13,7 +13,7 @@ export async function executeCommand (cmd: string, args: any[], cwd = '', consol
|
|
|
13
13
|
}
|
|
14
14
|
return await new Promise(resolve => {
|
|
15
15
|
const cmdstr = cmd + ' ' + args.join(' ')
|
|
16
|
-
// console.log('executing ', cmdstr, 'at', cwd)
|
|
16
|
+
// console.log('executing ', cmdstr, 'at', cwd, {consolePass})
|
|
17
17
|
const opts = {
|
|
18
18
|
cwd,
|
|
19
19
|
env: Object.assign(env, process.env)
|
|
@@ -6,14 +6,21 @@ import fs from 'fs'
|
|
|
6
6
|
import path from 'path'
|
|
7
7
|
import { resolvePaths } from '../pathResolve'
|
|
8
8
|
import * as ac from 'ansi-colors'
|
|
9
|
+
import { decoratedName, getAccountId } from '../IdSrc'
|
|
10
|
+
import { getAWSCredentials, getSettings } from '../LiftConfig'
|
|
11
|
+
import { parseConstraints, TypeConstraint } from '../TypeCheck'
|
|
12
|
+
import yaml from 'js-yaml'
|
|
9
13
|
|
|
10
14
|
export async function buildOpenApi (
|
|
11
15
|
defs: any[],
|
|
12
16
|
includePrivate: boolean = false,
|
|
17
|
+
includeCORS: boolean = false,
|
|
13
18
|
yamlFile?: string
|
|
14
19
|
): Promise<Uint8Array> {
|
|
15
20
|
const builder = new OpenApiBuilder()
|
|
16
21
|
|
|
22
|
+
// console.log("buildOpenApi to "+yamlFile)
|
|
23
|
+
|
|
17
24
|
const projectPaths = await resolvePaths()
|
|
18
25
|
if (!projectPaths.verified) return new Uint8Array(0) // don't continue if not valid
|
|
19
26
|
|
|
@@ -67,12 +74,14 @@ export async function buildOpenApi (
|
|
|
67
74
|
addTypeSchema(builder, schemaName, schema)
|
|
68
75
|
}
|
|
69
76
|
method = method.trim().toLowerCase()
|
|
70
|
-
addFunctionMethod(pathDef, method, def)
|
|
77
|
+
await addFunctionMethod(pathDef, method, def, includeCORS)
|
|
71
78
|
for (const param of parameters) {
|
|
72
79
|
addParameter(pathDef, param)
|
|
73
80
|
}
|
|
74
|
-
addCORSOptionMethod(pathDef)
|
|
75
|
-
|
|
81
|
+
if (includeCORS) addCORSOptionMethod(pathDef)
|
|
82
|
+
let pathMap = def.pathMap ?? '/' + (def.name as string)
|
|
83
|
+
pathMap = pathMap.replace('?', '')
|
|
84
|
+
|
|
76
85
|
// pathMap = stagePathSegment + pathMap
|
|
77
86
|
builder.addPath(pathMap, pathDef)
|
|
78
87
|
|
|
@@ -93,72 +102,230 @@ export async function buildOpenApi (
|
|
|
93
102
|
return bufView
|
|
94
103
|
}
|
|
95
104
|
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
const spec = builder.getSpec()
|
|
106
|
+
spec['x-amazon-apigateway-binary-media-types'] = [
|
|
107
|
+
'application/octet-stream',
|
|
108
|
+
'image/*',
|
|
109
|
+
'audio/*'
|
|
110
|
+
]
|
|
111
|
+
const yamlSpec = yaml.dump(spec)
|
|
112
|
+
const bytes = str2ab(yamlSpec)
|
|
113
|
+
// if (!includePrivate) {
|
|
114
|
+
fs.writeFileSync(outFile, yamlSpec)
|
|
115
|
+
// }
|
|
101
116
|
return bytes
|
|
102
117
|
}
|
|
103
118
|
|
|
104
119
|
function addTypeSchema (builder: any, schemaName: string, schema: any): void {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
120
|
+
let primType = ''
|
|
121
|
+
let type = schema?.type ?? 'Empty'
|
|
122
|
+
if (type === 'empty') type = 'Empty' // fix case
|
|
123
|
+
const isArray = (type.endsWith('[]'))
|
|
124
|
+
if (isArray) type = type.substring(0, type.length - 2)
|
|
125
|
+
if (type === 'string' || type === 'number' || type === 'object') primType = type
|
|
126
|
+
const mime = schema?.mime ?? primType ? 'text/plain' : 'application/json'
|
|
127
|
+
const sref = primType ? { type: primType } : { $ref: '#/components/schemas/' + type }
|
|
128
|
+
let ref: any = {}
|
|
129
|
+
if (isArray) {
|
|
130
|
+
ref = { type: 'array', items: sref }
|
|
131
|
+
} else {
|
|
132
|
+
ref = sref
|
|
110
133
|
}
|
|
111
134
|
|
|
135
|
+
ref.title = schemaName
|
|
136
|
+
|
|
137
|
+
if (ref.type === 'object') ref.properties = {}
|
|
138
|
+
|
|
139
|
+
const constraints = schema.type !== 'object' && Array.isArray(schema?.constraints) ? parseConstraints(schema.type, schema.constraints.join('\n'), '\n') : undefined
|
|
140
|
+
|
|
141
|
+
const cdesc = (constraints != null) ? '\n - ' + (constraints.describe() ?? '').split('\n').join('\n - ') : ''
|
|
142
|
+
let description = schema.description
|
|
143
|
+
if (cdesc) description += cdesc
|
|
144
|
+
|
|
145
|
+
ref.description = description
|
|
146
|
+
|
|
147
|
+
const required: string[] = []
|
|
148
|
+
|
|
149
|
+
if (ref.properties) {
|
|
150
|
+
for (const [propName, propDef] of Object.entries(schema.properties || {})) {
|
|
151
|
+
const pda = propDef as any
|
|
152
|
+
const constraints = Array.isArray(pda?.constraints) ? parseConstraints(pda.type, pda.constraints.join('\n'), '\n') : undefined
|
|
153
|
+
|
|
154
|
+
const cdesc = (constraints != null) ? '\n - ' + (constraints.describe() ?? '').split('\n').join('\n - ') : ''
|
|
155
|
+
let description = (propDef as any).description
|
|
156
|
+
if (cdesc) description += cdesc
|
|
157
|
+
|
|
158
|
+
// if(constraints) {
|
|
159
|
+
// console.log("schema definition for "+schemaName+', prop '+propName)
|
|
160
|
+
// console.log(description)
|
|
161
|
+
// }
|
|
162
|
+
|
|
163
|
+
const propSchema: any = {
|
|
164
|
+
type: (propDef as any).type,
|
|
165
|
+
description
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// You can add other custom handling here (e.g., enums, format, pattern)
|
|
169
|
+
ref.properties[propName] = propSchema
|
|
170
|
+
|
|
171
|
+
// Only mark as required if declared explicitly
|
|
172
|
+
if (schema.required && schema.required.includes(propName)) {
|
|
173
|
+
required.push(propName)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (required.length > 0) {
|
|
178
|
+
ref.required = required
|
|
179
|
+
}
|
|
180
|
+
}
|
|
112
181
|
builder.addSchema(schemaName, ref)
|
|
113
182
|
}
|
|
114
183
|
|
|
115
|
-
function addFunctionMethod (pathDef: any, method: string, def: any): void {
|
|
116
|
-
// TODO: Define a return schema and put that here
|
|
184
|
+
async function addFunctionMethod (pathDef: any, method: string, def: any, includeCORS = true): Promise<void> {
|
|
117
185
|
const retDef: any = (def.returns)['200']
|
|
118
186
|
const content: any = {}
|
|
119
|
-
|
|
187
|
+
let primType = ''
|
|
188
|
+
let type = retDef?.type ?? 'Empty'
|
|
189
|
+
if (type === 'empty') type = 'Empty' // fix case
|
|
190
|
+
const isArray = (type.endsWith('[]'))
|
|
191
|
+
if (isArray) type = type.substring(0, type.length - 2)
|
|
192
|
+
if (type === 'string' || type === 'number' || type === 'object') primType = type
|
|
193
|
+
const mime = retDef?.mime ?? primType ? 'text/plain' : 'application/json'
|
|
194
|
+
const ref = primType ? { type: primType } : { $ref: '#/components/schemas/' + type }
|
|
195
|
+
let schema: any
|
|
196
|
+
if (isArray) {
|
|
197
|
+
schema = { type: 'array', items: ref }
|
|
198
|
+
} else {
|
|
199
|
+
schema = ref
|
|
200
|
+
}
|
|
120
201
|
content[mime] = {
|
|
202
|
+
schema
|
|
121
203
|
}
|
|
122
204
|
|
|
205
|
+
const region = getSettings()?.awsPreferredRegion ?? ''
|
|
206
|
+
const accountId = await getAccountId()
|
|
207
|
+
const decName = decoratedName(def.name)
|
|
208
|
+
|
|
209
|
+
const isBinary = def.bodyType && !def.bodyType.startsWith('text') && !def.bodyType.endsWith('json')
|
|
210
|
+
|
|
123
211
|
const methData = {
|
|
124
212
|
summary: def.name,
|
|
125
213
|
description: def.description,
|
|
126
214
|
responses: {
|
|
127
215
|
200: {
|
|
128
216
|
description: retDef?.description ?? 'Success Response',
|
|
217
|
+
headers: includeCORS
|
|
218
|
+
? {
|
|
219
|
+
'Access-Control-Allow-Origin': {
|
|
220
|
+
schema: {
|
|
221
|
+
type: 'string'
|
|
222
|
+
},
|
|
223
|
+
example: '*'
|
|
224
|
+
},
|
|
225
|
+
'Access-Control-Allow-Headers': {
|
|
226
|
+
schema: {
|
|
227
|
+
type: 'string'
|
|
228
|
+
},
|
|
229
|
+
example: 'Content-Type, Authorization'
|
|
230
|
+
},
|
|
231
|
+
'Access-Control-Allow-Methods': {
|
|
232
|
+
schema: {
|
|
233
|
+
type: 'string'
|
|
234
|
+
},
|
|
235
|
+
example: 'GET,POST,PUT,OPTIONS'
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
: undefined,
|
|
129
239
|
content
|
|
130
240
|
}
|
|
241
|
+
},
|
|
242
|
+
'x-amazon-apigateway-integration': {
|
|
243
|
+
uri: `arn:aws:apigateway:${region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${region}:${accountId}:function:${decName}/invocations`,
|
|
244
|
+
passthroughbehavior: 'when_no_match',
|
|
245
|
+
contentHandling: isBinary ? 'CONVERT_TO_BINARY' : undefined,
|
|
246
|
+
httpMethod: 'POST', // always POST - this is how API gateway calls Lambda, not the method of the api
|
|
247
|
+
type: 'aws_proxy'
|
|
131
248
|
}
|
|
132
249
|
}
|
|
250
|
+
|
|
251
|
+
if (!includeCORS) {
|
|
252
|
+
// get the other response code declarations
|
|
253
|
+
for (const rcode of Object.getOwnPropertyNames(def.returns)) {
|
|
254
|
+
if (rcode != '200') {
|
|
255
|
+
const retDef = def.returns[rcode] ?? {}
|
|
256
|
+
|
|
257
|
+
const content: any = {}
|
|
258
|
+
let primType = ''
|
|
259
|
+
let type = retDef?.type ?? 'Empty'
|
|
260
|
+
if (type === 'empty') type = 'Empty' // fix case
|
|
261
|
+
const isArray = (type.endsWith('[]'))
|
|
262
|
+
if (isArray) type = type.substring(0, type.length - 2)
|
|
263
|
+
if (type === 'string' || type === 'number' || type === 'object') primType = type
|
|
264
|
+
const mime = retDef?.mime ?? primType ? 'text/plain' : 'application/json'
|
|
265
|
+
const ref = primType ? { type: primType } : { $ref: '#/components/schemas/' + type }
|
|
266
|
+
let schema: any
|
|
267
|
+
if (isArray) {
|
|
268
|
+
schema = { type: 'array', items: ref }
|
|
269
|
+
} else {
|
|
270
|
+
schema = ref
|
|
271
|
+
}
|
|
272
|
+
content[mime] = {
|
|
273
|
+
schema
|
|
274
|
+
}
|
|
275
|
+
retDef.content = content;
|
|
276
|
+
(methData.responses as any)[rcode] = retDef
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
133
281
|
pathDef[method] = methData
|
|
134
282
|
}
|
|
135
283
|
function addCORSOptionMethod (pathDef: any): void {
|
|
136
|
-
if (pathDef.options === undefined) return // already assinged by definition
|
|
284
|
+
// if (pathDef.options === undefined) return // already assinged by definition
|
|
137
285
|
// add options for CORS
|
|
138
286
|
pathDef.options = {
|
|
287
|
+
summary: 'CORS support',
|
|
139
288
|
responses: {
|
|
140
289
|
200: {
|
|
141
|
-
description: '
|
|
142
|
-
|
|
143
|
-
'
|
|
290
|
+
description: 'CORS response',
|
|
291
|
+
headers: {
|
|
292
|
+
'Access-Control-Allow-Origin': {
|
|
144
293
|
schema: {
|
|
145
|
-
|
|
146
|
-
}
|
|
294
|
+
type: 'string'
|
|
295
|
+
},
|
|
296
|
+
example: '*'
|
|
297
|
+
},
|
|
298
|
+
'Access-Control-Allow-Methods': {
|
|
299
|
+
schema: {
|
|
300
|
+
type: 'string'
|
|
301
|
+
},
|
|
302
|
+
example: 'GET,POST,PUT,OPTIONS'
|
|
303
|
+
},
|
|
304
|
+
'Access-Control-Allow-Headers': {
|
|
305
|
+
schema: {
|
|
306
|
+
type: 'string'
|
|
307
|
+
},
|
|
308
|
+
example: 'Content-Type, Authorization'
|
|
147
309
|
}
|
|
148
310
|
}
|
|
149
311
|
}
|
|
150
312
|
},
|
|
151
313
|
'x-amazon-apigateway-integration': {
|
|
314
|
+
type: 'mock',
|
|
315
|
+
requestTemplates: {
|
|
316
|
+
'application/json': '{"statusCode": 200}'
|
|
317
|
+
},
|
|
152
318
|
responses: {
|
|
153
319
|
default: {
|
|
154
|
-
statusCode: '200'
|
|
320
|
+
statusCode: '200',
|
|
321
|
+
responseParameters: {
|
|
322
|
+
'method.response.header.Access-Control-Allow-Origin': "'*'",
|
|
323
|
+
'method.response.header.Access-Control-Allow-Methods': "'GET, POST, PUT, OPTIONS'",
|
|
324
|
+
'method.response.header.Access-Control-Allow-Headers': "'Content-Type, Authorization'"
|
|
325
|
+
}
|
|
155
326
|
}
|
|
156
327
|
},
|
|
157
|
-
|
|
158
|
-
'application/json': '{"statusCode": 200}'
|
|
159
|
-
},
|
|
160
|
-
passthroughBehavior: 'when_no_match',
|
|
161
|
-
type: 'mock'
|
|
328
|
+
passthroughBehavior: 'when_no_match'
|
|
162
329
|
}
|
|
163
330
|
}
|
|
164
331
|
}
|
|
@@ -166,21 +333,37 @@ function addCORSOptionMethod (pathDef: any): void {
|
|
|
166
333
|
function addParameter (pathDef: any, param: any): void {
|
|
167
334
|
if (pathDef.parameters === undefined) pathDef.parameters = []
|
|
168
335
|
const parameters = pathDef.parameters
|
|
169
|
-
const example = param.example ??
|
|
170
|
-
const type = param.type ??
|
|
171
|
-
const deflt = param.default ??
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
336
|
+
const example = param.example ?? ''
|
|
337
|
+
const type = param.type ?? 'string'
|
|
338
|
+
const deflt = param.default ?? ''
|
|
339
|
+
|
|
340
|
+
// parameter is always required if it comes from path
|
|
341
|
+
// never required if it comes from query
|
|
342
|
+
// and may or may not be if it comes from body (per def)
|
|
343
|
+
const src = param?.in?.trim().toLowerCase() ?? ''
|
|
344
|
+
let required = (src === 'path')
|
|
345
|
+
if (src === 'body') required = param.required ?? false
|
|
346
|
+
|
|
347
|
+
const constraints = Array.isArray(param.constraints) ? parseConstraints(param.type, param.constraints.join('\n'), '\n') : undefined
|
|
348
|
+
|
|
349
|
+
const cdesc = (constraints != null) ? '\n - ' + (constraints.describe() ?? '').split('\n').join('\n - ') : ''
|
|
350
|
+
let description = param.description
|
|
351
|
+
if (cdesc) description += cdesc
|
|
352
|
+
|
|
353
|
+
// don't declare parameters marked as 'body'. OpenAPI doesn't support that.
|
|
354
|
+
if (param.in === 'path' || param.in === 'query') {
|
|
355
|
+
parameters.push({
|
|
356
|
+
in: param.in,
|
|
357
|
+
name: param.name,
|
|
358
|
+
description,
|
|
359
|
+
example: example || undefined,
|
|
360
|
+
required,
|
|
361
|
+
schema: schemaType(deflt, type, true)
|
|
362
|
+
})
|
|
363
|
+
}
|
|
181
364
|
}
|
|
182
365
|
|
|
183
|
-
function schemaType (deflt: string, namedType: string, innerOnly: boolean): any {
|
|
366
|
+
function schemaType (deflt: string | undefined, namedType: string, innerOnly: boolean): any {
|
|
184
367
|
if (typeof namedType === 'object') return namedType
|
|
185
368
|
const typeFormat = namedType.split(':')
|
|
186
369
|
let type = typeFormat[0]
|
|
@@ -204,5 +387,6 @@ function schemaType (deflt: string, namedType: string, innerOnly: boolean): any
|
|
|
204
387
|
}
|
|
205
388
|
if (type === 'int') type = 'integer'
|
|
206
389
|
if (type === 'bool') type = 'boolean'
|
|
390
|
+
if (!deflt) deflt = undefined
|
|
207
391
|
return innerOnly ? { type, format, example: deflt } : { schema: { type, format, example: deflt } }
|
|
208
392
|
}
|
package/src/lift.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
LambdaApi,
|
|
3
|
+
getAssetUrl,
|
|
4
|
+
Success,
|
|
5
|
+
NotFound,
|
|
6
|
+
NotImplemented,
|
|
7
|
+
ServerError,
|
|
8
|
+
} from "@tremho/inverse-y"
|
|
2
9
|
import fs from "fs"
|
|
3
10
|
import path from 'path'
|
|
4
11
|
import {Log} from "@tremho/inverse-y"
|