@platformatic/generators 1.28.1 → 1.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.
package/index.js CHANGED
@@ -3,9 +3,10 @@
3
3
  const { BaseGenerator } = require('./lib/base-generator')
4
4
  const { StackableGenerator } = require('./lib/stackable-generator')
5
5
  const { generateTests } = require('./lib/create-plugin')
6
-
6
+ const utils = require('./lib/utils')
7
7
  module.exports = {
8
8
  BaseGenerator,
9
9
  StackableGenerator,
10
- generateTests
10
+ generateTests,
11
+ ...utils
11
12
  }
@@ -101,6 +101,7 @@ export namespace BaseGenerator {
101
101
 
102
102
  addPackage(pkg: PackageDefinition): Promise<void>
103
103
 
104
+ loadFromDir(dir: string): Promise<void>
104
105
  prepare(): Promise<GeneratorMetadata>
105
106
  run(): Promise<GeneratorMetadata>
106
107
  addQuestion(question: any, where?: WhereClause): Promise<void>
@@ -15,6 +15,9 @@ const { generateTests, generatePlugins } = require('./create-plugin')
15
15
  const { PrepareError, MissingEnvVariable, ModuleNeeded } = require('./errors')
16
16
  const { generateGitignore } = require('./create-gitignore')
17
17
  const generateName = require('boring-name-generator')
18
+ const { getServiceTemplateFromSchemaUrl } = require('./utils')
19
+ const { flattenObject } = require('./utils')
20
+ const { envStringToObject } = require('./utils')
18
21
  /* c8 ignore start */
19
22
  const fakeLogger = {
20
23
  info: () => {},
@@ -57,7 +60,8 @@ class BaseGenerator extends FileGenerator {
57
60
  isRuntimeContext: false,
58
61
  serviceName: '',
59
62
  envPrefix: '',
60
- env: {}
63
+ env: {},
64
+ isUpdating: false
61
65
  }
62
66
  }
63
67
 
@@ -179,47 +183,64 @@ class BaseGenerator extends FileGenerator {
179
183
  async prepare () {
180
184
  try {
181
185
  this.reset()
182
- await this.getFastifyVersion()
183
- await this.getPlatformaticVersion()
184
186
 
185
- await this._beforePrepare()
186
-
187
- // generate package.json
188
- const template = await this.generatePackageJson()
189
- this.addFile({
190
- path: '',
191
- file: 'package.json',
192
- contents: JSON.stringify(template, null, 2)
193
- })
194
-
195
- await this.generateConfigFile()
187
+ if (this.config.isUpdating) {
188
+ // only the packages options may have changed, let's update those
189
+ await this.generateConfigFile()
190
+ const generatedConfigFile = JSON.parse(this.getFileObject('platformatic.json', '').contents)
191
+ const fileFromDisk = await this.loadFile({ file: 'platformatic.json', path: '' })
192
+ const currentConfigFile = JSON.parse(fileFromDisk.contents)
193
+ if (generatedConfigFile.plugins && generatedConfigFile.plugins.packages) {
194
+ currentConfigFile.plugins.packages = generatedConfigFile.plugins.packages
195
+ }
196
+ this.reset()
197
+ this.addFile({
198
+ path: '',
199
+ file: 'platformatic.json',
200
+ contents: JSON.stringify(currentConfigFile, null, 2)
201
+ })
202
+ } else {
203
+ await this.getFastifyVersion()
204
+ await this.getPlatformaticVersion()
196
205
 
197
- await this.generateEnv()
206
+ await this._beforePrepare()
198
207
 
199
- if (this.config.typescript) {
200
- // create tsconfig.json
208
+ // generate package.json
209
+ const template = await this.generatePackageJson()
201
210
  this.addFile({
202
211
  path: '',
203
- file: 'tsconfig.json',
204
- contents: JSON.stringify(this.getTsConfig(), null, 2)
212
+ file: 'package.json',
213
+ contents: JSON.stringify(template, null, 2)
205
214
  })
206
- }
207
215
 
208
- if (this.config.plugin) {
209
- // create plugin
210
- this.files.push(...generatePlugins(this.config.typescript))
211
- if (this.config.tests) {
212
- // create tests
213
- this.files.push(...generateTests(this.config.typescript, this.module))
216
+ await this.generateConfigFile()
217
+
218
+ await this.generateEnv()
219
+
220
+ if (this.config.typescript) {
221
+ // create tsconfig.json
222
+ this.addFile({
223
+ path: '',
224
+ file: 'tsconfig.json',
225
+ contents: JSON.stringify(this.getTsConfig(), null, 2)
226
+ })
214
227
  }
215
- }
216
228
 
217
- this.files.push(generateGitignore())
229
+ if (this.config.plugin) {
230
+ // create plugin
231
+ this.files.push(...generatePlugins(this.config.typescript))
232
+ if (this.config.tests) {
233
+ // create tests
234
+ this.files.push(...generateTests(this.config.typescript, this.module))
235
+ }
236
+ }
218
237
 
219
- await this._afterPrepare()
238
+ this.files.push(generateGitignore())
220
239
 
221
- this.checkEnvVariablesInConfigFile()
240
+ await this._afterPrepare()
222
241
 
242
+ this.checkEnvVariablesInConfigFile()
243
+ }
223
244
  return {
224
245
  targetDirectory: this.targetDirectory,
225
246
  env: this.config.env
@@ -310,7 +331,6 @@ class BaseGenerator extends FileGenerator {
310
331
  async generateConfigFile () {
311
332
  const configFileName = 'platformatic.json'
312
333
  const contents = await this._getConfigFileContents()
313
-
314
334
  // handle packages
315
335
  if (this.packages.length > 0) {
316
336
  if (!contents.plugins) {
@@ -431,6 +451,48 @@ class BaseGenerator extends FileGenerator {
431
451
  this.packages.push(pkg)
432
452
  }
433
453
 
454
+ async loadFromDir (serviceName, runtimeRootPath) {
455
+ const runtimePkgConfigFileData = JSON.parse(await readFile(join(runtimeRootPath, 'platformatic.json'), 'utf-8'))
456
+ const servicesPath = runtimePkgConfigFileData.autoload.path
457
+ const servicePkgJsonFileData = JSON.parse(await readFile(join(runtimeRootPath, servicesPath, serviceName, 'platformatic.json'), 'utf-8'))
458
+ const runtimeEnv = envStringToObject(await readFile(join(runtimeRootPath, '.env'), 'utf-8'))
459
+ const serviceNamePrefix = convertServiceNameToPrefix(serviceName)
460
+ const plugins = []
461
+ if (servicePkgJsonFileData.plugins && servicePkgJsonFileData.plugins.packages) {
462
+ for (const pkg of servicePkgJsonFileData.plugins.packages) {
463
+ const flattened = flattenObject(pkg)
464
+ const output = {
465
+ name: flattened.name,
466
+ options: []
467
+ }
468
+ if (pkg.options) {
469
+ Object.entries(flattened)
470
+ .filter(([key, value]) => key.indexOf('options.') === 0 && flattened[key].startsWith('{PLT_'))
471
+ .forEach(([key, value]) => {
472
+ const runtimeEnvVarKey = value.replace(/[{}]/g, '')
473
+ const serviceEnvVarKey = runtimeEnvVarKey.replace(`PLT_${serviceNamePrefix}_`, '')
474
+ const option = {
475
+ name: serviceEnvVarKey,
476
+ path: key.replace('options.', ''),
477
+ type: 'string',
478
+ value: runtimeEnv[runtimeEnvVarKey]
479
+ }
480
+ output.options.push(option)
481
+ })
482
+ }
483
+
484
+ plugins.push(output)
485
+ }
486
+ }
487
+
488
+ return {
489
+ name: serviceName,
490
+ template: getServiceTemplateFromSchemaUrl(servicePkgJsonFileData.$schema),
491
+ fields: [],
492
+ plugins
493
+ }
494
+ }
495
+
434
496
  // implement in the subclass
435
497
  /* c8 ignore next 1 */
436
498
  async postInstallActions () {}
@@ -23,6 +23,7 @@ export namespace FileGenerator {
23
23
  reset(): void
24
24
  writeFiles(): Promise<void>
25
25
  listFiles(): FileObject
26
+ loadFile(): Promise<FileObject>
26
27
  getFileObject(file: string, path?: string): FileObject
27
28
  }
28
29
 
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
  const { safeMkdir } = require('./utils')
3
3
  const { join } = require('node:path')
4
- const { writeFile } = require('node:fs/promises')
4
+ const { writeFile, readFile } = require('node:fs/promises')
5
5
 
6
6
  /* c8 ignore start */
7
7
  const fakeLogger = {
@@ -24,6 +24,13 @@ class FileGenerator {
24
24
  this.targetDirectory = dir
25
25
  }
26
26
 
27
+ async loadFile ({ path, file }) {
28
+ const filePath = join(this.targetDirectory, path, file)
29
+ const contents = await readFile(filePath, 'utf-8')
30
+ this.addFile({ path, file, contents })
31
+ return this.getFileObject(file, path)
32
+ }
33
+
27
34
  addFile ({ path, file, contents, options = {} }) {
28
35
  const fileObject = this.getFileObject(file, path)
29
36
  if (path.startsWith('/')) {
package/lib/utils.d.ts CHANGED
@@ -13,7 +13,10 @@ export namespace GeneratorUtils {
13
13
  export function stripVersion(version: string): string
14
14
  export function convertServiceNameToPrefix(serviceName: string): string
15
15
  export function envObjectToString(env: Env): string
16
+ export function envStringToObject(env: string): Env
16
17
  export function extractEnvVariablesFromText(text: string): string[]
17
18
  export function getPackageConfigurationObject(config: PackageConfiguration[]): object
19
+ export function flattenObject(obj: object): object
20
+ export function getServiceTemplateFromSchemaUrl(schemaUrl: string): string
18
21
  export const PLT_ROOT: string
19
22
  }
package/lib/utils.js CHANGED
@@ -6,7 +6,7 @@ const { join } = require('node:path')
6
6
  const { request } = require('undici')
7
7
  const { setTimeout } = require('timers/promises')
8
8
  const PLT_ROOT = 'PLT_ROOT'
9
-
9
+ const { EOL } = require('node:os')
10
10
  async function safeMkdir (dir) {
11
11
  try {
12
12
  await mkdir(dir, { recursive: true })
@@ -52,9 +52,22 @@ function envObjectToString (env) {
52
52
  Object.entries(env).forEach((kv) => {
53
53
  output.push(`${kv[0]}=${kv[1]}`)
54
54
  })
55
- return output.join('\n')
55
+ return output.join(EOL)
56
56
  }
57
57
 
58
+ function envStringToObject (envString) {
59
+ const output = {}
60
+ const split = envString.split(EOL)
61
+ split
62
+ .filter((line) => {
63
+ return line.trim() !== '' && line.indexOf('#') !== 0
64
+ })
65
+ .forEach((line) => {
66
+ const kv = line.split('=')
67
+ output[kv[0]] = kv[1]
68
+ })
69
+ return output
70
+ }
58
71
  function extractEnvVariablesFromText (text) {
59
72
  const match = text.match(/\{[a-zA-Z0-9-_]*\}/g)
60
73
  if (match) {
@@ -132,13 +145,53 @@ async function getLatestNpmVersion (pkg) {
132
145
  }
133
146
  return null
134
147
  }
148
+ /**
149
+ * Flatten a deep-nested object to a single level depth one
150
+ * i.e from
151
+ * {
152
+ * name: 'test',
153
+ * a: {
154
+ * b: {
155
+ * c: 'foobar'
156
+ * }
157
+ * }
158
+ * }
159
+ * to:
160
+ * {
161
+ * name: 'test',
162
+ * 'a.b.c': 'foobar'
163
+ * }
164
+ * @param {Object} ob
165
+ * @returns Object
166
+ */
167
+ function flattenObject (ob) {
168
+ const result = {}
169
+ for (const i in ob) {
170
+ if ((typeof ob[i]) === 'object' && !Array.isArray(ob[i])) {
171
+ const temp = flattenObject(ob[i])
172
+ for (const j in temp) {
173
+ result[i + '.' + j] = temp[j]
174
+ }
175
+ } else {
176
+ result[i] = ob[i]
177
+ }
178
+ }
179
+ return result
180
+ }
135
181
 
182
+ function getServiceTemplateFromSchemaUrl (schemaUrl) {
183
+ const splitted = schemaUrl.split('/')
184
+ return `@platformatic/${splitted[splitted.length - 1]}`
185
+ }
136
186
  module.exports = {
137
187
  addPrefixToString,
138
188
  convertServiceNameToPrefix,
139
189
  getPackageConfigurationObject,
140
190
  envObjectToString,
191
+ envStringToObject,
141
192
  extractEnvVariablesFromText,
193
+ flattenObject,
194
+ getServiceTemplateFromSchemaUrl,
142
195
  safeMkdir,
143
196
  stripVersion,
144
197
  PLT_ROOT,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/generators",
3
- "version": "1.28.1",
3
+ "version": "1.29.0",
4
4
  "description": "Main classes and utils for generators.",
5
5
  "main": "index.js",
6
6
  "keywords": [],
@@ -1,18 +1,13 @@
1
1
  'use strict'
2
2
 
3
- const { readFile, rm } = require('node:fs/promises')
4
- const { test, afterEach, describe } = require('node:test')
3
+ const { readFile, rm, cp } = require('node:fs/promises')
4
+ const { test, after, afterEach, describe } = require('node:test')
5
5
  const assert = require('node:assert')
6
6
  const { join } = require('node:path')
7
7
 
8
- const { fakeLogger, getTempDir } = require('./helpers')
8
+ const { fakeLogger, getTempDir, moveToTmpdir, mockNpmJsRequestForPkgs, mockAgent } = require('./helpers')
9
9
  const { BaseGenerator } = require('../lib/base-generator')
10
10
  const { convertServiceNameToPrefix } = require('../lib/utils')
11
- const { MockAgent, setGlobalDispatcher } = require('undici')
12
-
13
- const mockAgent = new MockAgent()
14
- setGlobalDispatcher(mockAgent)
15
- mockAgent.disableNetConnect()
16
11
 
17
12
  afterEach(async () => {
18
13
  try {
@@ -104,7 +99,8 @@ test('setConfig', async (t) => {
104
99
  isRuntimeContext: false,
105
100
  serviceName: '',
106
101
  envPrefix: '',
107
- tests: false
102
+ tests: false,
103
+ isUpdating: false
108
104
  })
109
105
 
110
106
  // should not have undefined properties
@@ -131,7 +127,8 @@ test('setConfig', async (t) => {
131
127
  isRuntimeContext: false,
132
128
  serviceName: '',
133
129
  envPrefix: '',
134
- tests: false
130
+ tests: false,
131
+ isUpdating: false
135
132
  })
136
133
 
137
134
  // reset config to defaults
@@ -150,7 +147,8 @@ test('setConfig', async (t) => {
150
147
  isRuntimeContext: false,
151
148
  serviceName: '',
152
149
  envPrefix: '',
153
- tests: false
150
+ tests: false,
151
+ isUpdating: false
154
152
  })
155
153
 
156
154
  // update only some fields
@@ -177,7 +175,8 @@ test('setConfig', async (t) => {
177
175
  isRuntimeContext: false,
178
176
  serviceName: '',
179
177
  envPrefix: '',
180
- tests: false
178
+ tests: false,
179
+ isUpdating: false
181
180
  })
182
181
  })
183
182
 
@@ -194,10 +193,10 @@ test('should append env values', async (t) => {
194
193
 
195
194
  await bg.prepare()
196
195
  const dotEnvFile = bg.getFileObject('.env')
197
- assert.equal(dotEnvFile.contents, 'FOO=bar\n')
196
+ assert.equal(dotEnvFile.contents.trim(), 'FOO=bar')
198
197
 
199
198
  const dotEnvSampleFile = bg.getFileObject('.env.sample')
200
- assert.equal(dotEnvSampleFile.contents, 'FOO=bar\n')
199
+ assert.equal(dotEnvSampleFile.contents.trim(), 'FOO=bar')
201
200
  })
202
201
 
203
202
  test('should prepare the questions', async (t) => {
@@ -584,18 +583,19 @@ test('support packages', async (t) => {
584
583
  }
585
584
 
586
585
  {
586
+ mockNpmJsRequestForPkgs(['foobar'])
587
587
  // should get the version from npm
588
- mockAgent
589
- .get('https://registry.npmjs.org')
590
- .intercept({
591
- method: 'GET',
592
- path: '/foobar'
593
- })
594
- .reply(200, {
595
- 'dist-tags': {
596
- latest: '1.42.0'
597
- }
598
- })
588
+ // mockAgent
589
+ // .get('https://registry.npmjs.org')
590
+ // .intercept({
591
+ // method: 'GET',
592
+ // path: '/foobar'
593
+ // })
594
+ // .reply(200, {
595
+ // 'dist-tags': {
596
+ // latest: '1.42.0'
597
+ // }
598
+ // })
599
599
 
600
600
  const svc = new BaseGenerator({
601
601
  module: '@platformatic/service'
@@ -720,7 +720,99 @@ test('support packages', async (t) => {
720
720
  assert.equal(packageJson.dependencies.foobar, 'latest')
721
721
  }
722
722
  })
723
+ test('should load data from directory', async (t) => {
724
+ const runtimeDirectory = join(__dirname, 'fixtures', 'sample-runtime')
725
+ const bg = new BaseGenerator({
726
+ module: '@platformatic/service'
727
+ })
728
+ const data = await bg.loadFromDir('rival', runtimeDirectory)
729
+ const expected = {
730
+ name: 'rival',
731
+ template: '@platformatic/service',
732
+ fields: [],
733
+ plugins: [
734
+ {
735
+ name: '@fastify/oauth2',
736
+ options: [
737
+ {
738
+ path: 'name',
739
+ type: 'string',
740
+ value: 'googleOAuth2',
741
+ name: 'FST_PLUGIN_OAUTH2_NAME'
742
+ },
743
+ {
744
+ path: 'credentials.client.id',
745
+ type: 'string',
746
+ value: 'sample_client_id',
747
+ name: 'FST_PLUGIN_OAUTH2_CREDENTIALS_CLIENT_ID'
748
+ },
749
+ {
750
+ path: 'credentials.client.secret',
751
+ type: 'string',
752
+ value: 'sample_client_secret',
753
+ name: 'FST_PLUGIN_OAUTH2_CREDENTIALS_CLIENT_SECRET'
754
+ },
755
+ {
756
+ path: 'startRedirectPath',
757
+ type: 'string',
758
+ value: '/login/google',
759
+ name: 'FST_PLUGIN_OAUTH2_REDIRECT_PATH'
760
+ },
761
+ {
762
+ path: 'callbackUri',
763
+ type: 'string',
764
+ value: 'http://localhost:3000/login/google/callback',
765
+ name: 'FST_PLUGIN_OAUTH2_CALLBACK_URI'
766
+ }
767
+ ]
768
+ }]
769
+ }
770
+ assert.deepEqual(data, expected)
771
+ })
772
+
773
+ test.only('on update should just touch the packages configuration', async (t) => {
774
+ mockNpmJsRequestForPkgs(['@fastify/foo-plugin'])
775
+ const runtimeDirectory = join(__dirname, 'fixtures', 'sample-runtime', 'services', 'rival')
776
+ const dir = await moveToTmpdir(after)
777
+ await cp(runtimeDirectory, dir, { recursive: true })
778
+
779
+ const bg = new BaseGenerator({
780
+ module: '@platformatic/service',
781
+ targetDirectory: dir
782
+ })
783
+ bg.setConfig({
784
+ isUpdating: true
785
+ })
786
+ await bg.addPackage({
787
+ name: '@fastify/foo-plugin',
788
+ options: [
789
+ {
790
+ path: 'name',
791
+ type: 'string',
792
+ value: 'foobar',
793
+ name: 'FST_PLUGIN_FOO_FOOBAR'
794
+ }
795
+ ]
796
+ })
797
+ await bg.prepare()
798
+
799
+ assert.equal(bg.files.length, 1)
800
+ assert.equal(bg.files[0].file, 'platformatic.json')
801
+ assert.equal(bg.files[0].path, '')
723
802
 
803
+ const configFileContents = JSON.parse(bg.files[0].contents)
804
+ assert.deepEqual(configFileContents.plugins.packages, [
805
+ {
806
+ name: '@fastify/foo-plugin',
807
+ options: {
808
+ name: '{FST_PLUGIN_FOO_FOOBAR}'
809
+ }
810
+ }
811
+ ])
812
+ assert.deepEqual(bg.config.dependencies, {
813
+ '@fastify/foo-plugin': '1.42.0'
814
+ })
815
+ })
724
816
  describe('runtime context', () => {
725
817
  test('should set config.envPrefix correctly', async (t) => {
726
818
  const bg = new BaseGenerator({
@@ -103,4 +103,38 @@ describe('FileGenerator', () => {
103
103
  assert.equal(err.code, 'ENOENT')
104
104
  }
105
105
  })
106
+
107
+ test('should load file from filesystem', async (t) => {
108
+ const tempDir = join(tmpdir(), `plt-file-generator-test-${dirCount++}`)
109
+ t.after(async () => {
110
+ await rm(tempDir, { recursive: true })
111
+ })
112
+
113
+ await safeMkdir(tempDir)
114
+ const fg = new FileGenerator()
115
+ fg.setTargetDirectory(tempDir)
116
+ fg.addFile({ path: 'myDir', file: 'helloworld.txt', contents: 'hello world' })
117
+ await fg.writeFiles()
118
+
119
+ fg.reset()
120
+ const fileObject = await fg.loadFile({
121
+ path: 'myDir',
122
+ file: 'helloworld.txt'
123
+ })
124
+
125
+ assert.deepEqual(fileObject, {
126
+ path: 'myDir',
127
+ file: 'helloworld.txt',
128
+ contents: 'hello world',
129
+ options: {}
130
+ })
131
+
132
+ assert.equal(fg.files.length, 1)
133
+ assert.deepEqual(fg.files[0], {
134
+ path: 'myDir',
135
+ file: 'helloworld.txt',
136
+ contents: 'hello world',
137
+ options: {}
138
+ })
139
+ })
106
140
  })
@@ -0,0 +1,9 @@
1
+ PLT_SERVER_HOSTNAME=0.0.0.0
2
+ PORT=3042
3
+ PLT_SERVER_LOGGER_LEVEL=info
4
+ PLT_RIVAL_TYPESCRIPT=true
5
+ PLT_RIVAL_FST_PLUGIN_OAUTH2_NAME=googleOAuth2
6
+ PLT_RIVAL_FST_PLUGIN_OAUTH2_CREDENTIALS_CLIENT_ID=sample_client_id
7
+ PLT_RIVAL_FST_PLUGIN_OAUTH2_CREDENTIALS_CLIENT_SECRET=sample_client_secret
8
+ PLT_RIVAL_FST_PLUGIN_OAUTH2_REDIRECT_PATH=/login/google
9
+ PLT_RIVAL_FST_PLUGIN_OAUTH2_CALLBACK_URI=http://localhost:3000/login/google/callback
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "https://platformatic.dev/schemas/v1.25.0/runtime",
3
+ "entrypoint": "rival",
4
+ "allowCycles": false,
5
+ "hotReload": true,
6
+ "autoload": {
7
+ "path": "services",
8
+ "exclude": [
9
+ "docs"
10
+ ]
11
+ },
12
+ "server": {
13
+ "hostname": "{PLT_SERVER_HOSTNAME}",
14
+ "port": "{PORT}",
15
+ "logger": {
16
+ "level": "{PLT_SERVER_LOGGER_LEVEL}"
17
+ }
18
+ }
19
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "$schema": "https://platformatic.dev/schemas/v1.25.0/service",
3
+ "service": {
4
+ "openapi": true
5
+ },
6
+ "watch": true,
7
+ "plugins": {
8
+ "paths": [
9
+ {
10
+ "path": "./plugins",
11
+ "encapsulate": false
12
+ },
13
+ "./routes"
14
+ ],
15
+ "typescript": "{PLT_RIVAL_TYPESCRIPT}",
16
+ "packages": [
17
+ {
18
+ "name": "@fastify/oauth2",
19
+ "options": {
20
+ "name": "{PLT_RIVAL_FST_PLUGIN_OAUTH2_NAME}",
21
+ "credentials": {
22
+ "client": {
23
+ "id": "{PLT_RIVAL_FST_PLUGIN_OAUTH2_CREDENTIALS_CLIENT_ID}",
24
+ "secret": "{PLT_RIVAL_FST_PLUGIN_OAUTH2_CREDENTIALS_CLIENT_SECRET}"
25
+ }
26
+ },
27
+ "startRedirectPath": "{PLT_RIVAL_FST_PLUGIN_OAUTH2_REDIRECT_PATH}",
28
+ "callbackUri": "{PLT_RIVAL_FST_PLUGIN_OAUTH2_CALLBACK_URI}"
29
+ }
30
+ }
31
+ ]
32
+ }
33
+ }
package/test/helpers.js CHANGED
@@ -3,6 +3,11 @@
3
3
  const { join } = require('node:path')
4
4
  const fs = require('node:fs/promises')
5
5
  const { safeMkdir } = require('../lib/utils')
6
+ const { MockAgent, setGlobalDispatcher } = require('undici')
7
+
8
+ const mockAgent = new MockAgent()
9
+ setGlobalDispatcher(mockAgent)
10
+ mockAgent.disableNetConnect()
6
11
 
7
12
  let counter = 0
8
13
 
@@ -16,21 +21,35 @@ async function getTempDir (baseDir) {
16
21
  }
17
22
  async function moveToTmpdir (teardown) {
18
23
  const cwd = process.cwd()
19
- const tmp = join(__dirname, 'tmp')
20
- try {
21
- await fs.mkdir(tmp)
22
- } catch {
23
- }
24
- const dir = await getTempDir(tmp)
25
- await fs.mkdir(dir)
24
+ // const tmp = join(__dirname, 'tmp')
25
+ // try {
26
+ // await fs.mkdir(tmp)
27
+ // } catch {
28
+ // }
29
+ const dir = await getTempDir()
26
30
  process.chdir(dir)
27
31
  teardown(() => process.chdir(cwd))
28
32
  if (!process.env.SKIP_RM_TMP) {
29
- teardown(() => fs.rm(tmp, { recursive: true }).catch(() => {}))
33
+ teardown(() => fs.rm(dir, { recursive: true }).catch(() => {}))
30
34
  }
31
35
  return dir
32
36
  }
33
37
 
38
+ function mockNpmJsRequestForPkgs (pkgs) {
39
+ for (const pkg of pkgs) {
40
+ mockAgent
41
+ .get('https://registry.npmjs.org')
42
+ .intercept({
43
+ method: 'GET',
44
+ path: `/${pkg}`
45
+ })
46
+ .reply(200, {
47
+ 'dist-tags': {
48
+ latest: '1.42.0'
49
+ }
50
+ })
51
+ }
52
+ }
34
53
  module.exports = {
35
54
  fakeLogger: {
36
55
  info: () => {},
@@ -39,5 +58,7 @@ module.exports = {
39
58
  error: () => {}
40
59
  },
41
60
  getTempDir,
42
- moveToTmpdir
61
+ moveToTmpdir,
62
+ mockNpmJsRequestForPkgs,
63
+ mockAgent
43
64
  }
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { test, describe } = require('node:test')
4
+ const { EOL } = require('node:os')
4
5
  const assert = require('node:assert')
5
6
  const {
6
7
  stripVersion,
@@ -10,6 +11,9 @@ const {
10
11
  getPackageConfigurationObject,
11
12
  addPrefixToString
12
13
  } = require('../lib/utils')
14
+ const { flattenObject } = require('../lib/utils')
15
+ const { getServiceTemplateFromSchemaUrl } = require('../lib/utils')
16
+ const { envStringToObject } = require('../lib/utils')
13
17
 
14
18
  describe('utils', () => {
15
19
  describe('stripVersion', async () => {
@@ -47,7 +51,7 @@ describe('utils', () => {
47
51
  DATABASE_URL: 'sqlite://./db.sqlite'
48
52
  }
49
53
 
50
- assert.equal(envObjectToString(env), 'FOO=bar\nDATABASE_URL=sqlite://./db.sqlite')
54
+ assert.equal(envObjectToString(env), `FOO=bar${EOL}DATABASE_URL=sqlite://./db.sqlite`)
51
55
  })
52
56
  })
53
57
 
@@ -158,4 +162,62 @@ describe('utils', () => {
158
162
  assert.equal(addPrefixToString('FOO', ''), 'FOO')
159
163
  })
160
164
  })
165
+
166
+ describe('flattenObject', () => {
167
+ test('should return a single depth object', () => {
168
+ const packageObject = {
169
+ name: '@fastify/oauth2',
170
+ options: {
171
+ name: '{PLT_RIVAL_FST_PLUGIN_OAUTH2_NAME}',
172
+ credentials: {
173
+ client: {
174
+ id: '{PLT_RIVAL_FST_PLUGIN_OAUTH2_CREDENTIALS_CLIENT_ID}',
175
+ secret: '{PLT_RIVAL_FST_PLUGIN_OAUTH2_CREDENTIALS_CLIENT_SECRET}'
176
+ }
177
+ },
178
+ startRedirectPath: '{PLT_RIVAL_FST_PLUGIN_OAUTH2_REDIRECT_PATH}',
179
+ callbackUri: '{PLT_RIVAL_FST_PLUGIN_OAUTH2_CALLBACK_URI}'
180
+ }
181
+ }
182
+ const expected = {
183
+ name: '@fastify/oauth2',
184
+ 'options.name': '{PLT_RIVAL_FST_PLUGIN_OAUTH2_NAME}',
185
+ 'options.credentials.client.id': '{PLT_RIVAL_FST_PLUGIN_OAUTH2_CREDENTIALS_CLIENT_ID}',
186
+ 'options.credentials.client.secret': '{PLT_RIVAL_FST_PLUGIN_OAUTH2_CREDENTIALS_CLIENT_SECRET}',
187
+ 'options.startRedirectPath': '{PLT_RIVAL_FST_PLUGIN_OAUTH2_REDIRECT_PATH}',
188
+ 'options.callbackUri': '{PLT_RIVAL_FST_PLUGIN_OAUTH2_CALLBACK_URI}'
189
+ }
190
+ assert.deepEqual(flattenObject(packageObject), expected)
191
+ })
192
+ })
193
+
194
+ describe('getServiceTemplateFromSchemaUrl', () => {
195
+ test('should get the right template name from schema url', () => {
196
+ const composerSchema = 'https://platformatic.dev/schemas/v1.25.0/composer'
197
+ const serviceSchema = 'https://platformatic.dev/schemas/v1.25.0/service'
198
+ const dbSchema = 'https://platformatic.dev/schemas/v1.25.0/db'
199
+
200
+ assert.equal(getServiceTemplateFromSchemaUrl(composerSchema), '@platformatic/composer')
201
+ assert.equal(getServiceTemplateFromSchemaUrl(serviceSchema), '@platformatic/service')
202
+ assert.equal(getServiceTemplateFromSchemaUrl(dbSchema), '@platformatic/db')
203
+ })
204
+ })
205
+
206
+ describe('envStringToObject', () => {
207
+ test('should convert .env-like string to object', () => {
208
+ const template = [
209
+ '',
210
+ '# this is a comment that will be not parsed',
211
+ 'MY_VAR=value',
212
+ 'PLT_SERVICE_NAME_FOOBAR=foobar'
213
+ ]
214
+
215
+ const expected = {
216
+ MY_VAR: 'value',
217
+ PLT_SERVICE_NAME_FOOBAR: 'foobar'
218
+ }
219
+
220
+ assert.deepEqual(envStringToObject(template.join(EOL)), expected)
221
+ })
222
+ })
161
223
  })