From c8c63bd472ac15bac472fc85eedcb83430da7836 Mon Sep 17 00:00:00 2001 From: Michael Goberling Date: Mon, 9 Mar 2026 12:33:06 -0400 Subject: [PATCH 1/2] feat: align action input schema with openwhisk --- schema/app.config.yaml.schema.json | 2 +- test/__fixtures__/app-exc-nui/app.config.yaml | 2 + test/__fixtures__/app/app.config.yaml | 2 + test/__fixtures__/legacy-app/manifest.yml | 2 + .../data-mocks/config-loader-include-index.js | 32 ++++++ test/data-mocks/config-loader.js | 7 ++ test/index.test.js | 107 ++++++++++++++++++ 7 files changed, 153 insertions(+), 1 deletion(-) diff --git a/schema/app.config.yaml.schema.json b/schema/app.config.yaml.schema.json index 4b764b5..3aeb4f2 100644 --- a/schema/app.config.yaml.schema.json +++ b/schema/app.config.yaml.schema.json @@ -147,7 +147,7 @@ "type": "object", "patternProperties": { "^[^\n]+$": { - "type": ["string", "boolean", "object"] + "type": ["string", "boolean", "number", "array", "object", "null"] } }, "additionalProperties": false diff --git a/test/__fixtures__/app-exc-nui/app.config.yaml b/test/__fixtures__/app-exc-nui/app.config.yaml index d242be9..89d26f8 100644 --- a/test/__fixtures__/app-exc-nui/app.config.yaml +++ b/test/__fixtures__/app-exc-nui/app.config.yaml @@ -13,6 +13,8 @@ application: runtime: 'nodejs:14' inputs: LOG_LEVEL: 'debug' + TAGS: ['tag1', 'tag2'] + MAX_ITEMS: 100 SOMETHING: type: string description: this is about something diff --git a/test/__fixtures__/app/app.config.yaml b/test/__fixtures__/app/app.config.yaml index f9d23f4..2dce036 100644 --- a/test/__fixtures__/app/app.config.yaml +++ b/test/__fixtures__/app/app.config.yaml @@ -13,6 +13,8 @@ application: runtime: 'nodejs:14' inputs: LOG_LEVEL: 'debug' + TAGS: ['tag1', 'tag2'] + MAX_ITEMS: 100 SOMETHING: type: string description: this is about something diff --git a/test/__fixtures__/legacy-app/manifest.yml b/test/__fixtures__/legacy-app/manifest.yml index 375db21..c7effc2 100644 --- a/test/__fixtures__/legacy-app/manifest.yml +++ b/test/__fixtures__/legacy-app/manifest.yml @@ -8,6 +8,8 @@ packages: runtime: 'nodejs:14' inputs: LOG_LEVEL: 'debug' + TAGS: ['tag1', 'tag2'] + MAX_ITEMS: 100 SOMETHING: type: string description: this is about something diff --git a/test/data-mocks/config-loader-include-index.js b/test/data-mocks/config-loader-include-index.js index 6c31dae..7f64329 100644 --- a/test/data-mocks/config-loader-include-index.js +++ b/test/data-mocks/config-loader-include-index.js @@ -179,6 +179,22 @@ const appIncludeIndex = { file: 'app.config.yaml', key: 'application.runtimeManifest.packages.my-app-package.actions.action.inputs.LOG_LEVEL' }, + 'application.runtimeManifest.packages.my-app-package.actions.action.inputs.MAX_ITEMS': { + file: 'app.config.yaml', + key: 'application.runtimeManifest.packages.my-app-package.actions.action.inputs.MAX_ITEMS' + }, + 'application.runtimeManifest.packages.my-app-package.actions.action.inputs.TAGS': { + file: 'app.config.yaml', + key: 'application.runtimeManifest.packages.my-app-package.actions.action.inputs.TAGS' + }, + 'application.runtimeManifest.packages.my-app-package.actions.action.inputs.TAGS.0': { + file: 'app.config.yaml', + key: 'application.runtimeManifest.packages.my-app-package.actions.action.inputs.TAGS.0' + }, + 'application.runtimeManifest.packages.my-app-package.actions.action.inputs.TAGS.1': { + file: 'app.config.yaml', + key: 'application.runtimeManifest.packages.my-app-package.actions.action.inputs.TAGS.1' + }, 'application.runtimeManifest.packages.my-app-package.actions.action.inputs.SOMETHING': { file: 'app.config.yaml', key: 'application.runtimeManifest.packages.my-app-package.actions.action.inputs.SOMETHING' @@ -638,6 +654,22 @@ const legacyIncludeIndex = { file: 'manifest.yml', key: 'packages.__APP_PACKAGE__.actions.action.inputs.LOG_LEVEL' }, + 'application.runtimeManifest.packages.__APP_PACKAGE__.actions.action.inputs.MAX_ITEMS': { + file: 'manifest.yml', + key: 'packages.__APP_PACKAGE__.actions.action.inputs.MAX_ITEMS' + }, + 'application.runtimeManifest.packages.__APP_PACKAGE__.actions.action.inputs.TAGS': { + file: 'manifest.yml', + key: 'packages.__APP_PACKAGE__.actions.action.inputs.TAGS' + }, + 'application.runtimeManifest.packages.__APP_PACKAGE__.actions.action.inputs.TAGS.0': { + file: 'manifest.yml', + key: 'packages.__APP_PACKAGE__.actions.action.inputs.TAGS.0' + }, + 'application.runtimeManifest.packages.__APP_PACKAGE__.actions.action.inputs.TAGS.1': { + file: 'manifest.yml', + key: 'packages.__APP_PACKAGE__.actions.action.inputs.TAGS.1' + }, 'application.runtimeManifest.packages.__APP_PACKAGE__.actions.action.inputs.SOMETHING': { file: 'manifest.yml', key: 'packages.__APP_PACKAGE__.actions.action.inputs.SOMETHING' diff --git a/test/data-mocks/config-loader.js b/test/data-mocks/config-loader.js index 1750ef4..846023d 100644 --- a/test/data-mocks/config-loader.js +++ b/test/data-mocks/config-loader.js @@ -46,6 +46,8 @@ function fullFakeRuntimeManifest (pathToActionFolder, pkgName1) { runtime: 'nodejs:14', inputs: { LOG_LEVEL: 'debug', + TAGS: ['tag1', 'tag2'], + MAX_ITEMS: 100, SOMETHING: { type: 'string', description: 'this is about something', @@ -434,6 +436,11 @@ module.exports = (appFixtureName, mockedAIOConfig, rewriteMockConfig = {}) => { } if (mockedAIOConfig && mockedAIOConfig.project && mockedAIOConfig.project.org) { config.all[k].imsOrgId = mockedAIOConfig.project.org.ims_org_id + } else { + config.all[k].imsOrgId = config.all[k].imsOrgId ?? undefined + } + if (config.all[k].app.hasFrontend && config.all[k].s3) { + config.all[k].s3.tvmUrl = config.all[k].s3.tvmUrl ?? undefined } config.all[k].ow.package = `${config.packagejson.name}-${config.packagejson.version}` config.all[k].app.name = config.packagejson.name diff --git a/test/index.test.js b/test/index.test.js index 116ca01..f0e323d 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -869,6 +869,113 @@ application: await expect(appConfig.load({})).rejects.toThrow('Missing or invalid keys in app.config.yaml') }) + test('valid schema: action inputs support array (OpenWhisk spec)', async () => { + global.fakeFileSystem.addJson( + { + '/package.json': '{}', + '/app.config.yaml': ` + application: + runtimeManifest: + packages: + pkg: + actions: + myaction: + function: actions/test.js + inputs: + TAGS: ['tag1', 'tag2', 'tag3'] + EMPTY: [] + ` + } + ) + const config = await appConfig.load({}) + const inputs = config.all.application.manifest.full.packages.pkg.actions.myaction.inputs + expect(inputs.TAGS).toEqual(['tag1', 'tag2', 'tag3']) + expect(Array.isArray(inputs.TAGS)).toBe(true) + expect(inputs.EMPTY).toEqual([]) + }) + + test('valid schema: action inputs support number (OpenWhisk spec)', async () => { + global.fakeFileSystem.addJson( + { + '/package.json': '{}', + '/app.config.yaml': ` + application: + runtimeManifest: + packages: + pkg: + actions: + myaction: + function: actions/test.js + inputs: + COUNT: 42 + NEGATIVE: -531 + FLOAT: 432.43 + ` + } + ) + const config = await appConfig.load({}) + const inputs = config.all.application.manifest.full.packages.pkg.actions.myaction.inputs + expect(inputs.COUNT).toBe(42) + expect(inputs.NEGATIVE).toBe(-531) + expect(inputs.FLOAT).toBe(432.43) + }) + + test('valid schema: action inputs support null (OpenWhisk spec)', async () => { + global.fakeFileSystem.addJson( + { + '/package.json': '{}', + '/app.config.yaml': ` + application: + runtimeManifest: + packages: + pkg: + actions: + myaction: + function: actions/test.js + inputs: + OPTIONAL: null + ` + } + ) + const config = await appConfig.load({}) + const inputs = config.all.application.manifest.full.packages.pkg.actions.myaction.inputs + expect(inputs.OPTIONAL).toBeNull() + }) + + test('valid schema: action inputs support mixed types (OpenWhisk spec)', async () => { + global.fakeFileSystem.addJson( + { + '/package.json': '{}', + '/app.config.yaml': ` + application: + runtimeManifest: + packages: + pkg: + actions: + myaction: + function: actions/test.js + inputs: + STR: hello + NUM: 1 + FLAG: true + ARR: [a, b] + OBJ: + type: string + default: '' + NIL: null + ` + } + ) + const config = await appConfig.load({}) + const inputs = config.all.application.manifest.full.packages.pkg.actions.myaction.inputs + expect(inputs.STR).toBe('hello') + expect(inputs.NUM).toBe(1) + expect(inputs.FLAG).toBe(true) + expect(inputs.ARR).toEqual(['a', 'b']) + expect(inputs.OBJ).toEqual({ type: 'string', default: '' }) + expect(inputs.NIL).toBeNull() + }) + test('valid schema with productDependencies', async () => { global.fakeFileSystem.addJson( { From 2566fb73e10f498f96200b2aeba858e0028ae7f8 Mon Sep 17 00:00:00 2001 From: Michael Goberling Date: Mon, 9 Mar 2026 12:46:30 -0400 Subject: [PATCH 2/2] feat: document package inputs too --- schema/app.config.yaml.schema.json | 1 + test/index.test.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/schema/app.config.yaml.schema.json b/schema/app.config.yaml.schema.json index 3aeb4f2..2ae4b31 100644 --- a/schema/app.config.yaml.schema.json +++ b/schema/app.config.yaml.schema.json @@ -113,6 +113,7 @@ "type": "object", "properties": { "license": { "type": "string" }, + "inputs": { "$ref": "#/definitions/inputs" }, "actions": { "$ref": "#/definitions/actions" } } }, diff --git a/test/index.test.js b/test/index.test.js index f0e323d..ab97f55 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -976,6 +976,36 @@ application: expect(inputs.NIL).toBeNull() }) + test('valid schema: package-level inputs (same constraints as action inputs)', async () => { + global.fakeFileSystem.addJson( + { + '/package.json': '{}', + '/app.config.yaml': ` + application: + runtimeManifest: + packages: + pkg: + license: Apache-2.0 + inputs: + PKG_LEVEL: pkg-default + PKG_TAGS: ['a', 'b'] + PKG_COUNT: 10 + actions: + myaction: + function: actions/test.js + inputs: + ACTION_INPUT: action-value + ` + } + ) + const config = await appConfig.load({}) + const pkg = config.all.application.manifest.full.packages.pkg + expect(pkg.inputs.PKG_LEVEL).toBe('pkg-default') + expect(pkg.inputs.PKG_TAGS).toEqual(['a', 'b']) + expect(pkg.inputs.PKG_COUNT).toBe(10) + expect(pkg.actions.myaction.inputs.ACTION_INPUT).toBe('action-value') + }) + test('valid schema with productDependencies', async () => { global.fakeFileSystem.addJson( {