@platformatic/composer 2.40.0 → 2.41.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/index.js +9 -7
- package/lib/openapi-composer.js +28 -6
- package/lib/openapi-generator.js +45 -5
- package/lib/openapi-scalar.js +23 -0
- package/package.json +11 -11
- package/schema.json +1 -1
- package/lib/openapi.js +0 -50
package/index.js
CHANGED
|
@@ -6,10 +6,9 @@ const { platformaticService, buildServer, buildStackable } = require('@platforma
|
|
|
6
6
|
|
|
7
7
|
const { schema } = require('./lib/schema')
|
|
8
8
|
const serviceProxy = require('./lib/proxy')
|
|
9
|
-
const openapi = require('./lib/openapi.js')
|
|
10
9
|
const graphql = require('./lib/graphql')
|
|
11
10
|
const composerHook = require('./lib/composer-hook')
|
|
12
|
-
const
|
|
11
|
+
const { openApiGenerator, openApiComposer } = require('./lib/openapi-generator')
|
|
13
12
|
const graphqlGenerator = require('./lib/graphql-generator')
|
|
14
13
|
const { isSameGraphqlSchema, fetchGraphqlSubgraphs } = require('./lib/graphql-fetch')
|
|
15
14
|
const notHostConstraints = require('./lib/proxy/not-host-constraints')
|
|
@@ -42,15 +41,18 @@ async function platformaticComposer (app, opts) {
|
|
|
42
41
|
}
|
|
43
42
|
}
|
|
44
43
|
|
|
44
|
+
await app.register(composerHook)
|
|
45
|
+
|
|
46
|
+
let generatedComposedOpenAPI = null
|
|
45
47
|
if (hasOpenapiServices) {
|
|
46
|
-
app
|
|
48
|
+
generatedComposedOpenAPI = await openApiGenerator(app, config.composer)
|
|
47
49
|
}
|
|
50
|
+
|
|
48
51
|
app.register(serviceProxy, { ...config.composer, context: opts.context })
|
|
49
|
-
app.register(
|
|
50
|
-
await app.register(platformaticService, { config, context: opts.context })
|
|
52
|
+
await app.register(platformaticService, { config: { ...config, openapi: false }, context: opts.context })
|
|
51
53
|
|
|
52
|
-
if (
|
|
53
|
-
await app.register(
|
|
54
|
+
if (generatedComposedOpenAPI) {
|
|
55
|
+
await app.register(openApiComposer, { opts: config.composer, generated: generatedComposedOpenAPI })
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
if (hasGraphqlServices) {
|
package/lib/openapi-composer.js
CHANGED
|
@@ -6,6 +6,7 @@ const errors = require('./errors')
|
|
|
6
6
|
function composeOpenApi (apis, options = {}) {
|
|
7
7
|
const mergedPaths = {}
|
|
8
8
|
const mergedSchemas = {}
|
|
9
|
+
const mergedSecuritySchemes = {}
|
|
9
10
|
|
|
10
11
|
for (const { id, prefix, schema } of apis) {
|
|
11
12
|
const { paths, components } = clone(schema)
|
|
@@ -15,6 +16,18 @@ function composeOpenApi (apis, options = {}) {
|
|
|
15
16
|
namespaceSchemaRefs(apiPrefix, pathSchema)
|
|
16
17
|
namespaceSchemaOperationIds(apiPrefix, pathSchema)
|
|
17
18
|
|
|
19
|
+
for (const methodSchema of Object.values(pathSchema)) {
|
|
20
|
+
if (methodSchema.security) {
|
|
21
|
+
methodSchema.security = methodSchema.security.map(security => {
|
|
22
|
+
const newSecurity = {}
|
|
23
|
+
for (const [securityKey, securityValue] of Object.entries(security)) {
|
|
24
|
+
newSecurity[apiPrefix + securityKey] = securityValue
|
|
25
|
+
}
|
|
26
|
+
return newSecurity
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
18
31
|
const mergedPath = prefix ? prefix + path : path
|
|
19
32
|
|
|
20
33
|
if (mergedPaths[mergedPath]) {
|
|
@@ -23,13 +36,21 @@ function composeOpenApi (apis, options = {}) {
|
|
|
23
36
|
mergedPaths[mergedPath] = pathSchema
|
|
24
37
|
}
|
|
25
38
|
|
|
26
|
-
if (components
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
schema.title
|
|
39
|
+
if (components) {
|
|
40
|
+
if (components.schemas) {
|
|
41
|
+
for (const [schemaKey, schema] of Object.entries(components.schemas)) {
|
|
42
|
+
if (schema.title == null) {
|
|
43
|
+
schema.title = schemaKey
|
|
44
|
+
}
|
|
45
|
+
namespaceSchemaRefs(apiPrefix, schema)
|
|
46
|
+
mergedSchemas[apiPrefix + schemaKey] = schema
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (components.securitySchemes) {
|
|
51
|
+
for (const [securitySchemeKey, securityScheme] of Object.entries(components.securitySchemes)) {
|
|
52
|
+
mergedSecuritySchemes[apiPrefix + securitySchemeKey] = securityScheme
|
|
30
53
|
}
|
|
31
|
-
namespaceSchemaRefs(apiPrefix, schema)
|
|
32
|
-
mergedSchemas[apiPrefix + schemaKey] = schema
|
|
33
54
|
}
|
|
34
55
|
}
|
|
35
56
|
}
|
|
@@ -41,6 +62,7 @@ function composeOpenApi (apis, options = {}) {
|
|
|
41
62
|
version: options.version || '1.0.0',
|
|
42
63
|
},
|
|
43
64
|
components: {
|
|
65
|
+
securitySchemes: mergedSecuritySchemes,
|
|
44
66
|
schemas: mergedSchemas,
|
|
45
67
|
},
|
|
46
68
|
paths: mergedPaths,
|
package/lib/openapi-generator.js
CHANGED
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
const { readFile } = require('node:fs/promises')
|
|
4
4
|
const { request, getGlobalDispatcher } = require('undici')
|
|
5
5
|
const fp = require('fastify-plugin')
|
|
6
|
-
const
|
|
6
|
+
const fastifySwagger = require('@fastify/swagger')
|
|
7
7
|
|
|
8
|
+
const errors = require('./errors')
|
|
8
9
|
const { modifyOpenApiSchema, originPathSymbol } = require('./openapi-modifier')
|
|
9
10
|
const composeOpenApi = require('./openapi-composer')
|
|
10
11
|
const loadOpenApiConfig = require('./openapi-load-config.js')
|
|
11
12
|
const { prefixWithSlash } = require('./utils.js')
|
|
13
|
+
const openApiScalar = require('./openapi-scalar')
|
|
12
14
|
|
|
13
15
|
async function fetchOpenApiSchema (openApiUrl) {
|
|
14
16
|
const { body } = await request(openApiUrl)
|
|
@@ -29,7 +31,7 @@ async function getOpenApiSchema (origin, openapi) {
|
|
|
29
31
|
return readOpenApiSchema(openapi.file)
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
async function
|
|
34
|
+
async function generateComposedOpenApi (app, opts) {
|
|
33
35
|
if (!opts.services.some(s => s.openapi)) { return }
|
|
34
36
|
|
|
35
37
|
const { services } = opts
|
|
@@ -72,9 +74,46 @@ async function composeOpenAPI (app, opts) {
|
|
|
72
74
|
openApiSchemas.push({ id, prefix, schema, originSchema, config: openapiConfig })
|
|
73
75
|
}
|
|
74
76
|
|
|
77
|
+
const composedOpenApiSchema = composeOpenApi(openApiSchemas, opts.openapi)
|
|
78
|
+
|
|
75
79
|
app.decorate('openApiSchemas', openApiSchemas)
|
|
80
|
+
app.decorate('composedOpenApiSchema', composedOpenApiSchema)
|
|
81
|
+
|
|
82
|
+
await app.register(fastifySwagger, {
|
|
83
|
+
exposeRoute: true,
|
|
84
|
+
openapi: {
|
|
85
|
+
info: {
|
|
86
|
+
title: opts.openapi?.title || 'Platformatic Composer',
|
|
87
|
+
version: opts.openapi?.version || '1.0.0'
|
|
88
|
+
},
|
|
89
|
+
servers: [{ url: globalThis.platformatic?.runtimeBasePath ?? '/' }],
|
|
90
|
+
components: app.composedOpenApiSchema.components
|
|
91
|
+
},
|
|
92
|
+
transform ({ schema, url }) {
|
|
93
|
+
for (const service of opts.services) {
|
|
94
|
+
if (!service.proxy) continue
|
|
95
|
+
|
|
96
|
+
const prefix = service.proxy.prefix ?? ''
|
|
97
|
+
const proxyPrefix = prefix.at(-1) === '/' ? prefix.slice(0, -1) : prefix
|
|
98
|
+
|
|
99
|
+
const proxyUrls = [proxyPrefix + '/', proxyPrefix + '/*']
|
|
100
|
+
if (proxyUrls.includes(url)) {
|
|
101
|
+
schema = schema ?? {}
|
|
102
|
+
schema.hide = true
|
|
103
|
+
break
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return { schema, url }
|
|
107
|
+
}
|
|
108
|
+
})
|
|
76
109
|
|
|
77
|
-
|
|
110
|
+
await app.register(openApiScalar, opts)
|
|
111
|
+
|
|
112
|
+
return { apiByApiRoutes }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function openApiComposer (app, { opts, generated }) {
|
|
116
|
+
const { apiByApiRoutes } = generated
|
|
78
117
|
|
|
79
118
|
const dispatcher = getGlobalDispatcher()
|
|
80
119
|
|
|
@@ -84,7 +123,7 @@ async function composeOpenAPI (app, opts) {
|
|
|
84
123
|
})
|
|
85
124
|
|
|
86
125
|
await app.register(await import('@platformatic/fastify-openapi-glue'), {
|
|
87
|
-
specification: composedOpenApiSchema,
|
|
126
|
+
specification: app.composedOpenApiSchema,
|
|
88
127
|
addEmptySchema: opts.addEmptySchema,
|
|
89
128
|
operationResolver: (operationId, method, openApiPath) => {
|
|
90
129
|
const { origin, prefix, schema } = apiByApiRoutes[openApiPath]
|
|
@@ -167,4 +206,5 @@ function generateRenamedPath (renamedOpenApiPath, routeParams) {
|
|
|
167
206
|
return renamedOpenApiPath.replace(/{(.*?)}/g, () => routeParams.shift())
|
|
168
207
|
}
|
|
169
208
|
|
|
170
|
-
module.exports =
|
|
209
|
+
module.exports.openApiGenerator = generateComposedOpenApi
|
|
210
|
+
module.exports.openApiComposer = fp(openApiComposer)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fp = require('fastify-plugin')
|
|
4
|
+
|
|
5
|
+
async function openApiScalar (app, opts) {
|
|
6
|
+
const { default: scalarTheme } = await import('@platformatic/scalar-theme')
|
|
7
|
+
|
|
8
|
+
/** Serve spec file in yaml and json */
|
|
9
|
+
app.get('/documentation/json', { schema: { hide: true } }, async () => app.swagger())
|
|
10
|
+
app.get('/documentation/yaml', { schema: { hide: true } }, async () => app.swagger({ yaml: true }))
|
|
11
|
+
|
|
12
|
+
const routePrefix = opts.openapi?.swaggerPrefix || '/documentation'
|
|
13
|
+
|
|
14
|
+
await app.register(require('@scalar/fastify-api-reference'), {
|
|
15
|
+
logLevel: 'warn',
|
|
16
|
+
routePrefix,
|
|
17
|
+
configuration: {
|
|
18
|
+
customCss: scalarTheme.theme
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = fp(openApiScalar)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/composer",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.41.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"typescript": "^5.5.4",
|
|
32
32
|
"why-is-node-running": "2",
|
|
33
33
|
"ws": "^8.16.0",
|
|
34
|
-
"@platformatic/client": "2.
|
|
35
|
-
"@platformatic/config": "2.
|
|
36
|
-
"@platformatic/db": "2.
|
|
34
|
+
"@platformatic/client": "2.41.0",
|
|
35
|
+
"@platformatic/config": "2.41.0",
|
|
36
|
+
"@platformatic/db": "2.41.0"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@fastify/error": "^4.0.0",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"@fastify/static": "^8.0.0",
|
|
43
43
|
"@fastify/swagger": "^9.0.0",
|
|
44
44
|
"@fastify/view": "^10.0.1",
|
|
45
|
-
"@platformatic/fastify-openapi-glue": "^5.
|
|
45
|
+
"@platformatic/fastify-openapi-glue": "^5.1.0",
|
|
46
46
|
"@platformatic/graphql-composer": "^0.10.0",
|
|
47
47
|
"@scalar/fastify-api-reference": "^1.19.5",
|
|
48
48
|
"ajv": "^8.12.0",
|
|
@@ -67,12 +67,12 @@
|
|
|
67
67
|
"rfdc": "^1.3.1",
|
|
68
68
|
"semgrator": "^0.3.0",
|
|
69
69
|
"undici": "^7.0.0",
|
|
70
|
-
"@platformatic/config": "2.
|
|
71
|
-
"@platformatic/
|
|
72
|
-
"@platformatic/
|
|
73
|
-
"@platformatic/
|
|
74
|
-
"@platformatic/telemetry": "2.
|
|
75
|
-
"@platformatic/
|
|
70
|
+
"@platformatic/config": "2.41.0",
|
|
71
|
+
"@platformatic/scalar-theme": "2.41.0",
|
|
72
|
+
"@platformatic/generators": "2.41.0",
|
|
73
|
+
"@platformatic/utils": "^2.41.0",
|
|
74
|
+
"@platformatic/telemetry": "2.41.0",
|
|
75
|
+
"@platformatic/service": "2.41.0"
|
|
76
76
|
},
|
|
77
77
|
"scripts": {
|
|
78
78
|
"test": "pnpm run lint && borp -T --timeout=300000 -c 1 && tsd",
|
package/schema.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://schemas.platformatic.dev/@platformatic/composer/2.
|
|
2
|
+
"$id": "https://schemas.platformatic.dev/@platformatic/composer/2.41.0.json",
|
|
3
3
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
4
4
|
"title": "Platformatic Composer",
|
|
5
5
|
"type": "object",
|
package/lib/openapi.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const fp = require('fastify-plugin')
|
|
4
|
-
|
|
5
|
-
async function composeOpenAPI (app, opts) {
|
|
6
|
-
await app.register(require('@fastify/swagger'), {
|
|
7
|
-
exposeRoute: true,
|
|
8
|
-
openapi: {
|
|
9
|
-
info: {
|
|
10
|
-
title: opts.openapi?.title || 'Platformatic Composer',
|
|
11
|
-
version: opts.openapi?.version || '1.0.0'
|
|
12
|
-
},
|
|
13
|
-
servers: [{ url: globalThis.platformatic?.runtimeBasePath ?? '/' }],
|
|
14
|
-
},
|
|
15
|
-
transform: ({ schema, url }) => {
|
|
16
|
-
for (const service of opts.services) {
|
|
17
|
-
if (!service.proxy) continue
|
|
18
|
-
|
|
19
|
-
const prefix = service.proxy.prefix ?? ''
|
|
20
|
-
const proxyPrefix = prefix.at(-1) === '/' ? prefix.slice(0, -1) : prefix
|
|
21
|
-
|
|
22
|
-
const proxyUrls = [proxyPrefix + '/', proxyPrefix + '/*']
|
|
23
|
-
if (proxyUrls.includes(url)) {
|
|
24
|
-
schema = schema ?? {}
|
|
25
|
-
schema.hide = true
|
|
26
|
-
break
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
return { schema, url }
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
const { default: scalarTheme } = await import('@platformatic/scalar-theme')
|
|
34
|
-
|
|
35
|
-
/** Serve spec file in yaml and json */
|
|
36
|
-
app.get('/documentation/json', { schema: { hide: true } }, async () => app.swagger())
|
|
37
|
-
app.get('/documentation/yaml', { schema: { hide: true } }, async () => app.swagger({ yaml: true }))
|
|
38
|
-
|
|
39
|
-
const routePrefix = opts.openapi?.swaggerPrefix || '/documentation'
|
|
40
|
-
|
|
41
|
-
await app.register(require('@scalar/fastify-api-reference'), {
|
|
42
|
-
logLevel: 'warn',
|
|
43
|
-
routePrefix,
|
|
44
|
-
configuration: {
|
|
45
|
-
customCss: scalarTheme.theme
|
|
46
|
-
}
|
|
47
|
-
})
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
module.exports = fp(composeOpenAPI)
|