@platformatic/service 0.28.1 → 0.29.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.
@@ -0,0 +1,34 @@
1
+ import { FastifyPluginAsync } from 'fastify'
2
+
3
+ interface GetRequest {
4
+ }
5
+
6
+ interface GetResponse {
7
+ }
8
+
9
+ interface Hello {
10
+ get(req: GetRequest): Promise<GetResponse>;
11
+ }
12
+
13
+ type HelloPlugin = FastifyPluginAsync<NonNullable<hello.HelloOptions>>
14
+
15
+ declare module 'fastify' {
16
+ interface FastifyInstance {
17
+ 'hello': Hello;
18
+ }
19
+
20
+ interface FastifyRequest {
21
+ 'hello': Hello;
22
+ }
23
+ }
24
+
25
+ declare namespace hello {
26
+ export interface HelloOptions {
27
+ url: string
28
+ }
29
+ export const hello: HelloPlugin;
30
+ export { hello as default };
31
+ }
32
+
33
+ declare function hello(...params: Parameters<HelloPlugin>): ReturnType<HelloPlugin>;
34
+ export = hello;
@@ -0,0 +1,22 @@
1
+ {
2
+ "openapi": "3.0.3",
3
+ "info": {
4
+ "title": "Platformatic",
5
+ "description": "This is a service built on top of Platformatic",
6
+ "version": "1.0.0"
7
+ },
8
+ "components": {
9
+ "schemas": {}
10
+ },
11
+ "paths": {
12
+ "/": {
13
+ "get": {
14
+ "responses": {
15
+ "200": {
16
+ "description": "Default Response"
17
+ }
18
+ }
19
+ }
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "server": {
3
+ "hostname": "127.0.0.1",
4
+ "port": 0,
5
+ "logger": {
6
+ "level": "warn",
7
+ "name": "hello client ts"
8
+ },
9
+ "pluginTimeout": 30000
10
+ },
11
+ "plugins": {
12
+ "paths": ["./plugin.ts"],
13
+ "typescript": true
14
+ },
15
+ "clients": [{
16
+ "schema": "./hello.openapi.json",
17
+ "name": "hello",
18
+ "type": "openapi",
19
+ "url": "{PLT_CLIENT_URL}"
20
+ }],
21
+ "metrics": false,
22
+ "watch": false
23
+ }
@@ -0,0 +1,8 @@
1
+ import { FastifyInstance } from 'fastify'
2
+ /// <reference path="./hello" />
3
+
4
+ export default async function (app: FastifyInstance) {
5
+ app.get('/', async () => {
6
+ return app.hello.get({})
7
+ })
8
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "esModuleInterop": true,
5
+ "target": "es6",
6
+ "moduleResolution": "node",
7
+ "sourceMap": true,
8
+ "pretty": true,
9
+ "noEmitOnError": true,
10
+ "outDir": "dist"
11
+ },
12
+ "watchOptions": {
13
+ "watchFile": "fixedPollingInterval",
14
+ "watchDirectory": "fixedPollingInterval",
15
+ "fallbackPolling": "dynamicPriority",
16
+ "synchronousWatchDirectory": true,
17
+ "excludeDirectories": [
18
+ "**/node_modules",
19
+ "dist"
20
+ ]
21
+ }
22
+ }
@@ -0,0 +1,34 @@
1
+ import { FastifyPluginAsync } from 'fastify'
2
+
3
+ interface GetRequest {
4
+ }
5
+
6
+ interface GetResponse {
7
+ }
8
+
9
+ interface Hello {
10
+ get(req: GetRequest): Promise<GetResponse>;
11
+ }
12
+
13
+ type HelloPlugin = FastifyPluginAsync<NonNullable<hello.HelloOptions>>
14
+
15
+ declare module 'fastify' {
16
+ interface FastifyInstance {
17
+ 'hello': Hello;
18
+ }
19
+
20
+ interface FastifyRequest {
21
+ 'hello': Hello;
22
+ }
23
+ }
24
+
25
+ declare namespace hello {
26
+ export interface HelloOptions {
27
+ url: string
28
+ }
29
+ export const hello: HelloPlugin;
30
+ export { hello as default };
31
+ }
32
+
33
+ declare function hello(...params: Parameters<HelloPlugin>): ReturnType<HelloPlugin>;
34
+ export = hello;
@@ -0,0 +1,22 @@
1
+ {
2
+ "openapi": "3.0.3",
3
+ "info": {
4
+ "title": "Platformatic",
5
+ "description": "This is a service built on top of Platformatic",
6
+ "version": "1.0.0"
7
+ },
8
+ "components": {
9
+ "schemas": {}
10
+ },
11
+ "paths": {
12
+ "/": {
13
+ "get": {
14
+ "responses": {
15
+ "200": {
16
+ "description": "Default Response"
17
+ }
18
+ }
19
+ }
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "server": {
3
+ "hostname": "127.0.0.1",
4
+ "port": 0,
5
+ "logger": {
6
+ "level": "warn",
7
+ "name": "hello client"
8
+ }
9
+ },
10
+ "plugins": {
11
+ "paths": ["./plugin.js"]
12
+ },
13
+ "clients": [{
14
+ "schema": "./hello.openapi.json",
15
+ "name": "hello",
16
+ "type": "openapi",
17
+ "url": "{PLT_CLIENT_URL}"
18
+ }],
19
+ "metrics": false,
20
+ "watch": false
21
+ }
@@ -0,0 +1,8 @@
1
+ 'use default'
2
+
3
+ /** @type {import('fastify').FastifyPluginAsync<{ optionA: boolean, optionB: string }>} */
4
+ module.exports = async function (app) {
5
+ app.get('/', async () => {
6
+ return app.hello.get()
7
+ })
8
+ }
package/help/compile.txt CHANGED
@@ -1,9 +1,10 @@
1
1
  Compile typescript plugins.
2
+
2
3
  ``` bash
3
4
  $ platformatic service compile
4
5
  ```
5
6
 
6
- As a result of executing this command, the Platformatic DB will compile typescript
7
+ As a result of executing this command, Platformatic Service will compile typescript
7
8
  plugins in the `outDir` directory.
8
9
 
9
10
  If not specified, the configuration specified will be loaded from
@@ -1,11 +1,15 @@
1
1
  'use strict'
2
2
 
3
3
  const fp = require('fastify-plugin')
4
+ const client = require('@platformatic/client')
4
5
 
5
6
  async function setupClients (app, opts) {
6
- const clientsConfig = opts
7
- for (const { path, url } of clientsConfig) {
8
- app.register(require(path), { url })
7
+ for (const { path, url, serviceId, name, schema, type } of opts) {
8
+ if (path) {
9
+ app.register(require(path), { url, serviceId })
10
+ } else {
11
+ app.register(client, { url, serviceId, name, path: schema, type })
12
+ }
9
13
  }
10
14
  }
11
15
 
@@ -12,24 +12,26 @@ async function loadPlugins (app) {
12
12
  const configManager = app.platformatic.configManager
13
13
  const config = configManager.current
14
14
 
15
- if (config.plugins.typescript) {
16
- const workingDir = configManager.dirname
17
- const tsConfigPath = join(workingDir, 'tsconfig.json')
18
-
19
- const isTsConfigAccessible = await isFileAccessible(tsConfigPath)
20
- if (!isTsConfigAccessible) {
21
- throw new Error('Cannot load typescript plugin, tsconfig.json not found')
22
- }
15
+ const workingDir = configManager.dirname
16
+ const tsConfigPath = join(workingDir, 'tsconfig.json')
23
17
 
18
+ // If the tsconfig.json file exists, then we need to adjust the plugin paths
19
+ // to point to the compiled JS files.
20
+ const isTsConfigAccessible = await isFileAccessible(tsConfigPath)
21
+ if (isTsConfigAccessible) {
24
22
  const tsConfig = JSON.parse(await readFile(tsConfigPath, 'utf8'))
25
23
  const outDir = resolve(workingDir, tsConfig.compilerOptions.outDir)
26
24
 
27
- config.plugins.paths = config.plugins.paths.map((plugin) => {
28
- /* c8 ignore next 3 */
29
- return typeof plugin === 'string'
30
- ? getJSPluginPath(workingDir, plugin, outDir)
31
- : { ...plugin, path: getJSPluginPath(workingDir, plugin.path, outDir) }
32
- })
25
+ const isOutDirAccessible = await isFileAccessible(outDir)
26
+
27
+ if (isOutDirAccessible) {
28
+ config.plugins.paths = config.plugins.paths.map((plugin) => {
29
+ /* c8 ignore next 3 */
30
+ return typeof plugin === 'string'
31
+ ? getJSPluginPath(workingDir, plugin, outDir)
32
+ : { ...plugin, path: getJSPluginPath(workingDir, plugin.path, outDir) }
33
+ })
34
+ }
33
35
  }
34
36
 
35
37
  await app.register(require(wrapperPath), { paths: config.plugins.paths })
package/lib/schema.js CHANGED
@@ -390,7 +390,11 @@ const plugins = {
390
390
  type: 'integer'
391
391
  },
392
392
  typescript: {
393
- type: 'boolean'
393
+ anyOf: [{
394
+ type: 'boolean'
395
+ }, {
396
+ type: 'string'
397
+ }]
394
398
  }
395
399
  },
396
400
  additionalProperties: false,
@@ -403,7 +407,12 @@ const metrics = {
403
407
  {
404
408
  type: 'object',
405
409
  properties: {
406
- port: { type: 'integer' },
410
+ port: {
411
+ anyOf: [
412
+ { type: 'integer' },
413
+ { type: 'string' }
414
+ ]
415
+ },
407
416
  hostname: { type: 'string' },
408
417
  auth: {
409
418
  type: 'object',
@@ -515,16 +524,26 @@ const clients = {
515
524
  serviceId: {
516
525
  type: 'string'
517
526
  },
527
+ name: {
528
+ type: 'string'
529
+ },
530
+ type: {
531
+ type: 'string',
532
+ enum: ['openapi', 'graphql']
533
+ },
518
534
  path: {
519
535
  type: 'string',
520
536
  resolvePath: true
521
537
  },
538
+ schema: {
539
+ type: 'string',
540
+ resolvePath: true
541
+ },
522
542
  url: {
523
543
  type: 'string'
524
544
  }
525
545
  },
526
- additionalProperties: false,
527
- required: ['path', 'url']
546
+ additionalProperties: false
528
547
  }
529
548
  }
530
549
 
@@ -538,6 +557,8 @@ const platformaticServiceSchema = {
538
557
  watch: {
539
558
  anyOf: [watch, {
540
559
  type: 'boolean'
560
+ }, {
561
+ type: 'string'
541
562
  }]
542
563
  },
543
564
  $schema: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/service",
3
- "version": "0.28.1",
3
+ "version": "0.29.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -64,11 +64,11 @@
64
64
  "pino-pretty": "^10.0.0",
65
65
  "rfdc": "^1.3.0",
66
66
  "ua-parser-js": "^1.0.35",
67
- "@platformatic/client": "0.28.1",
68
- "@platformatic/config": "0.28.1",
69
- "@platformatic/swagger-ui-theme": "0.28.1",
70
- "@platformatic/types": "0.28.1",
71
- "@platformatic/utils": "0.28.1"
67
+ "@platformatic/client": "0.29.0",
68
+ "@platformatic/config": "0.29.0",
69
+ "@platformatic/swagger-ui-theme": "0.29.0",
70
+ "@platformatic/types": "0.29.0",
71
+ "@platformatic/utils": "0.29.0"
72
72
  },
73
73
  "standard": {
74
74
  "ignore": [
@@ -284,3 +284,100 @@ t.test('should compile typescript plugin with start command from a folder', asyn
284
284
  }
285
285
  t.fail('should compile typescript plugin with start command')
286
286
  })
287
+
288
+ t.test('should start the service if it was precompiled and typescript is `false`', async (t) => {
289
+ const testDir = path.join(urlDirname(import.meta.url), '..', 'fixtures', 'typescript-plugin-nocompile')
290
+ const cwd = path.join(urlDirname(import.meta.url), '..', 'tmp', 'typescript-plugin-clone-9')
291
+
292
+ await cp(testDir, cwd, { recursive: true })
293
+
294
+ await execa('node', [cliPath, 'compile'], { cwd })
295
+
296
+ const child = execa('node', [cliPath, 'start'], { cwd })
297
+
298
+ const splitter = split()
299
+ child.stdout.pipe(splitter)
300
+
301
+ for await (const data of splitter) {
302
+ const sanitized = stripAnsi(data)
303
+ if (sanitized.includes('Typescript plugin loaded')) {
304
+ t.pass()
305
+ return
306
+ }
307
+ }
308
+ t.fail('should load the typescript plugin without compiling it')
309
+ })
310
+
311
+ t.test('should not start the service if it was not precompiled and typescript is `false`', async (t) => {
312
+ const testDir = path.join(urlDirname(import.meta.url), '..', 'fixtures', 'typescript-plugin-nocompile')
313
+ const cwd = path.join(urlDirname(import.meta.url), '..', 'tmp', 'typescript-plugin-clone-10')
314
+
315
+ await cp(testDir, cwd, { recursive: true })
316
+
317
+ const child = execa('node', [cliPath, 'start'], { cwd })
318
+
319
+ const splitter = split()
320
+ child.stdout.pipe(splitter)
321
+ child.stderr.pipe(splitter)
322
+
323
+ for await (const data of splitter) {
324
+ const sanitized = stripAnsi(data)
325
+ if (sanitized.includes('Unknown file extension ".ts" for')) {
326
+ t.pass()
327
+ return
328
+ }
329
+ }
330
+ t.fail('should load the typescript plugin without compiling it')
331
+ })
332
+
333
+ t.test('should compile typescript plugin with string config', async (t) => {
334
+ const testDir = path.join(urlDirname(import.meta.url), '..', 'fixtures', 'typescript-plugin-string')
335
+ const cwd = path.join(urlDirname(import.meta.url), '..', 'tmp', 'typescript-plugin-clone-11')
336
+
337
+ await cp(testDir, cwd, { recursive: true })
338
+
339
+ const child = execa('node', [cliPath, 'compile'], { cwd })
340
+
341
+ t.teardown(exitOnTeardown(child))
342
+
343
+ const splitter = split()
344
+ child.stdout.pipe(splitter)
345
+
346
+ for await (const data of splitter) {
347
+ const sanitized = stripAnsi(data)
348
+ if (sanitized.includes('Typescript compilation completed successfully.')) {
349
+ const jsPluginPath = path.join(cwd, 'dist', 'plugin.js')
350
+ try {
351
+ await access(jsPluginPath)
352
+ } catch (err) {
353
+ t.fail(err)
354
+ }
355
+
356
+ t.pass()
357
+ return
358
+ }
359
+ }
360
+ t.fail('should compile typescript plugin with a compile command')
361
+ })
362
+
363
+ t.test('should not start the service if it was not precompiled and typescript is `"false"`', async (t) => {
364
+ const testDir = path.join(urlDirname(import.meta.url), '..', 'fixtures', 'typescript-plugin-nocompile')
365
+ const cwd = path.join(urlDirname(import.meta.url), '..', 'tmp', 'typescript-plugin-clone-12')
366
+
367
+ await cp(testDir, cwd, { recursive: true })
368
+
369
+ const child = execa('node', [cliPath, 'start'], { cwd })
370
+
371
+ const splitter = split()
372
+ child.stdout.pipe(splitter)
373
+ child.stderr.pipe(splitter)
374
+
375
+ for await (const data of splitter) {
376
+ const sanitized = stripAnsi(data)
377
+ if (sanitized.includes('Unknown file extension ".ts" for')) {
378
+ t.pass()
379
+ return
380
+ }
381
+ }
382
+ t.fail('should load the typescript plugin without compiling it')
383
+ })
@@ -60,3 +60,56 @@ test('client is loaded (ts)', async ({ teardown, equal, pass, same }) => {
60
60
  const data = await res.body.json()
61
61
  same(data, { hello: 'world' })
62
62
  })
63
+
64
+ test('client is loaded dependencyless', async ({ teardown, equal, same }) => {
65
+ const app1 = await buildServer(join(__dirname, '..', 'fixtures', 'hello', 'warn-log.service.json'))
66
+
67
+ teardown(async () => {
68
+ await app1.close()
69
+ })
70
+ await app1.start()
71
+
72
+ process.env.PLT_CLIENT_URL = app1.url
73
+
74
+ const app2 = await buildServer(join(__dirname, '..', 'fixtures', 'hello-client-without-deps', 'platformatic.service.json'))
75
+
76
+ teardown(async () => {
77
+ await app2.close()
78
+ })
79
+ await app2.start()
80
+
81
+ const res = await request(`${app2.url}/`)
82
+ equal(res.statusCode, 200, 'status code')
83
+ const data = await res.body.json()
84
+ same(data, { hello: 'world' })
85
+ })
86
+
87
+ test('client is loaded (ts) depencyless', async ({ teardown, equal, pass, same }) => {
88
+ const app1 = await buildServer(join(__dirname, '..', 'fixtures', 'hello', 'warn-log.service.json'))
89
+
90
+ teardown(async () => {
91
+ await app1.close()
92
+ })
93
+ await app1.start()
94
+
95
+ process.env.PLT_CLIENT_URL = app1.url
96
+
97
+ const targetDir = join(__dirname, '..', 'fixtures', 'hello-client-ts-without-deps')
98
+
99
+ try {
100
+ await rmdir(join(targetDir, 'dist'))
101
+ } catch {}
102
+
103
+ await compile(targetDir, { server: { logger: { level: 'warn' } } })
104
+
105
+ const app2 = await buildServer(join(targetDir, 'platformatic.service.json'))
106
+ teardown(async () => {
107
+ await app2.close()
108
+ })
109
+ await app2.start()
110
+
111
+ const res = await request(`${app2.url}/`)
112
+ equal(res.statusCode, 200, 'status code')
113
+ const data = await res.body.json()
114
+ same(data, { hello: 'world' })
115
+ })
@@ -0,0 +1,16 @@
1
+ {
2
+ "server": {
3
+ "logger": {
4
+ "level": "info"
5
+ },
6
+ "hostname": "127.0.0.1",
7
+ "port": "0",
8
+ "pluginTimeout": 60000,
9
+ "keepAliveTimeout": 1
10
+ },
11
+ "plugins": {
12
+ "paths": ["plugin.ts"],
13
+ "typescript": "false"
14
+ },
15
+ "watch": false
16
+ }
@@ -0,0 +1,5 @@
1
+ import { FastifyInstance } from 'fastify'
2
+
3
+ export default async function (app: FastifyInstance) {
4
+ app.log.info('Typescript plugin loaded')
5
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "esModuleInterop": true,
5
+ "target": "es6",
6
+ "moduleResolution": "node",
7
+ "sourceMap": true,
8
+ "pretty": true,
9
+ "noEmitOnError": true,
10
+ "outDir": "dist"
11
+ },
12
+ "watchOptions": {
13
+ "watchFile": "fixedPollingInterval",
14
+ "watchDirectory": "fixedPollingInterval",
15
+ "fallbackPolling": "dynamicPriority",
16
+ "synchronousWatchDirectory": true,
17
+ "excludeDirectories": [
18
+ "**/node_modules",
19
+ "dist"
20
+ ]
21
+ }
22
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "server": {
3
+ "logger": {
4
+ "level": "info"
5
+ },
6
+ "hostname": "127.0.0.1",
7
+ "port": "0",
8
+ "pluginTimeout": 60000,
9
+ "keepAliveTimeout": 1
10
+ },
11
+ "plugins": {
12
+ "paths": ["plugin.ts"],
13
+ "typescript": "true"
14
+ },
15
+ "watch": false
16
+ }
@@ -0,0 +1,5 @@
1
+ import { FastifyInstance } from 'fastify'
2
+
3
+ export default async function (app: FastifyInstance) {
4
+ app.log.info('Typescript plugin loaded')
5
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "esModuleInterop": true,
5
+ "target": "es6",
6
+ "moduleResolution": "node",
7
+ "sourceMap": true,
8
+ "pretty": true,
9
+ "noEmitOnError": true,
10
+ "outDir": "dist"
11
+ },
12
+ "watchOptions": {
13
+ "watchFile": "fixedPollingInterval",
14
+ "watchDirectory": "fixedPollingInterval",
15
+ "fallbackPolling": "dynamicPriority",
16
+ "synchronousWatchDirectory": true,
17
+ "excludeDirectories": [
18
+ "**/node_modules",
19
+ "dist"
20
+ ]
21
+ }
22
+ }