From 899dd65e73feffcfee5505cc0afc28f9d7e8fe97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Narciso?= Date: Thu, 15 May 2025 15:58:46 -0300 Subject: [PATCH 1/8] feat(iac): edge storage and build bindings --- .../src/configProcessor/helpers/schema.ts | 142 +++++++++++++++--- .../configProcessor/helpers/schemaManifest.ts | 90 ++++++++++- .../functionsProcessConfigStrategy.ts | 17 +++ .../storageProcessConfigStrategy.ts | 39 +++++ .../configProcessor/processStrategy/index.ts | 2 + packages/config/src/types.ts | 22 +++ 6 files changed, 287 insertions(+), 25 deletions(-) create mode 100644 packages/config/src/configProcessor/processStrategy/implementations/storageProcessConfigStrategy.ts diff --git a/packages/config/src/configProcessor/helpers/schema.ts b/packages/config/src/configProcessor/helpers/schema.ts index 4880d56c..68ea80d1 100644 --- a/packages/config/src/configProcessor/helpers/schema.ts +++ b/packages/config/src/configProcessor/helpers/schema.ts @@ -424,6 +424,86 @@ const createRuleSchema = (isRequestPhase = false) => ({ }, }); +const schemaStorage = { + type: 'object', + properties: { + name: { + type: 'string', + minLength: 6, + maxLength: 63, + pattern: '^.{6,63}$', + errorMessage: "The 'name' field must be a string between 6 and 63 characters.", + }, + path: { + type: 'string', + errorMessage: "The 'path' field must be a string.", + }, + edgeAcess: { + type: 'string', + enum: ['read_only', 'read_write', 'restricted'], + errorMessage: "The 'edge_access' field must be one of: read_only, read_write, restricted.", + }, + }, + required: ['name', 'path'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in storage items.', + required: "The 'name' and 'edge_access' fields are required.", + }, +}; + +const schemaFunction = { + type: 'object', + properties: { + name: { + type: 'string', + errorMessage: "The 'name' field must be a string", + }, + path: { + type: 'string', + errorMessage: "The 'path' field must be a string", + }, + args: { + type: 'object', + errorMessage: "The 'args' field must be an object", + }, + bindings: { + type: 'object', + properties: { + storage: { + type: 'array', + items: { + type: 'object', + properties: { + bucket: { + type: 'string', + errorMessage: "The 'bucket' field must be a string", + }, + prefix: { + type: 'string', + errorMessage: "The 'prefix' field must be a string", + }, + }, + required: ['bucket'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in storage items', + required: "The 'bucket' field is required", + }, + }, + errorMessage: "The 'storage' field must be an array of storage bindings", + }, + }, + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in bindings object', + }, + }, + }, + required: ['name', 'path'], + additionalProperties: false, +}; + const azionConfigSchema = { $id: 'azionConfig', definitions: { @@ -446,6 +526,38 @@ const azionConfigSchema = { enum: ['webpack', 'esbuild'], errorMessage: "The 'build.bundler' must be either 'webpack' or 'esbuild'", }, + bindings: { + type: 'object', + properties: { + edgeStorage: { + type: 'array', + items: { + type: 'object', + properties: { + bucket: { + type: 'string', + errorMessage: "The 'bucket' field must be a string", + }, + prefix: { + type: 'string', + errorMessage: "The 'prefix' field must be a string", + }, + }, + required: ['bucket'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in edgeStorage items', + required: "The 'bucket' field are required", + }, + }, + errorMessage: "The 'edgeStorage' field must be an array of storage bindings", + }, + }, + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in bindings object', + }, + }, preset: { anyOf: [ { type: 'string' }, @@ -520,30 +632,7 @@ const azionConfigSchema = { }, functions: { type: 'array', - items: { - type: 'object', - properties: { - name: { - type: 'string', - errorMessage: "The function's 'name' field must be a string", - }, - path: { - type: 'string', - errorMessage: "The function's 'path' field must be a string", - }, - args: { - type: 'object', - additionalProperties: true, - errorMessage: "The function's 'args' field must be an object", - }, - }, - required: ['name', 'path'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in function items', - required: "Both 'name' and 'path' fields are required for each function", - }, - }, + items: schemaFunction, }, rules: { type: 'object', @@ -1295,6 +1384,11 @@ const azionConfigSchema = { type: "The 'waf' field must be an array", }, }, + storage: { + type: 'array', + items: schemaStorage, + errorMessage: "The 'storage' field must be an array of storage items.", + }, }, additionalProperties: false, errorMessage: { diff --git a/packages/config/src/configProcessor/helpers/schemaManifest.ts b/packages/config/src/configProcessor/helpers/schemaManifest.ts index 41d9e665..e0fce696 100644 --- a/packages/config/src/configProcessor/helpers/schemaManifest.ts +++ b/packages/config/src/configProcessor/helpers/schemaManifest.ts @@ -719,7 +719,33 @@ const schemaApplicationRules = { additionalProperties: false, }; -// ... resto do arquivo mantido como está ... +const schemaStorageManifest = { + type: 'object', + properties: { + name: { + type: 'string', + minLength: 6, + maxLength: 63, + pattern: '^.{6,63}$', + errorMessage: "The 'name' field must be a string between 6 and 63 characters.", + }, + path: { + type: 'string', + errorMessage: "The 'path' field must be a string.", + }, + edge_access: { + type: 'string', + enum: ['read_only', 'read_write', 'restricted'], + errorMessage: "The 'edge_access' field must be one of: read_only, read_write, restricted.", + }, + }, + required: ['name', 'path'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in storage items.', + required: "The 'name' field are required.", + }, +}; const schemaApplicationManifest = { type: 'object', @@ -806,6 +832,58 @@ const schemaApplicationManifest = { }, }; +const schemaFunctionManifest = { + type: 'object', + properties: { + name: { + type: 'string', + errorMessage: "The 'name' field must be a string", + }, + target: { + type: 'string', + errorMessage: "The 'target' field must be a string", + }, + args: { + type: 'object', + errorMessage: "The 'args' field must be an object", + }, + bindings: { + type: 'object', + properties: { + edge_storage: { + type: 'array', + items: { + type: 'object', + properties: { + bucket: { + type: 'string', + errorMessage: "The 'bucket' field must be a string", + }, + prefix: { + type: 'string', + errorMessage: "The 'prefix' field must be a string", + }, + }, + required: ['bucket'], + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in edge_storage items', + required: "The 'bucket' field is required", + }, + }, + errorMessage: "The 'edge_storage' field must be an array of storage bindings", + }, + }, + additionalProperties: false, + errorMessage: { + additionalProperties: 'No additional properties are allowed in bindings object', + }, + }, + }, + required: ['name', 'target'], + additionalProperties: false, +}; + const schemaManifest = { type: 'object', properties: { @@ -838,6 +916,16 @@ const schemaManifest = { items: schemaApplicationManifest, errorMessage: "The 'application' field must be an array of application items.", }, + storage: { + type: 'array', + items: schemaStorageManifest, + errorMessage: "The 'storage' field must be an array of storage items.", + }, + function: { + type: 'array', + items: schemaFunctionManifest, + errorMessage: "The 'function' field must be an array of function items.", + }, }, }; export { schemaManifest }; diff --git a/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts index 1168428a..4c9525e2 100644 --- a/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts +++ b/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts @@ -16,6 +16,14 @@ class FunctionsProcessConfigStrategy extends ProcessConfigStrategy { name: func.name, target: func.path, args: func.args || {}, + bindings: func.bindings + ? { + edge_storage: func.bindings.storage?.map((storage) => ({ + bucket: storage.bucket, + prefix: storage.prefix, + })), + } + : undefined, })); } @@ -29,6 +37,15 @@ class FunctionsProcessConfigStrategy extends ProcessConfigStrategy { name: func.name, path: func.target, args: func.args || {}, + bindings: func.bindings + ? { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + storage: func.bindings.edge_storage?.map((storage: any) => ({ + bucket: storage.bucket, + prefix: storage.prefix, + })), + } + : undefined, })); return transformedPayload.functions; diff --git a/packages/config/src/configProcessor/processStrategy/implementations/storageProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/storageProcessConfigStrategy.ts new file mode 100644 index 00000000..3ea5c1cd --- /dev/null +++ b/packages/config/src/configProcessor/processStrategy/implementations/storageProcessConfigStrategy.ts @@ -0,0 +1,39 @@ +import { AzionConfig } from '../../../types'; +import ProcessConfigStrategy from '../processConfigStrategy'; + +/** + * StorageProcessConfigStrategy + * @class StorageProcessConfigStrategy + * @description This class is implementation of the Storage ProcessConfig Strategy. + */ +class StorageProcessConfigStrategy extends ProcessConfigStrategy { + transformToManifest(config: AzionConfig) { + if (!Array.isArray(config?.storage) || config?.storage.length === 0) { + return; + } + + return config.storage.map((item) => ({ + name: item.name, + edge_access: item.edgeAccess || 'read_only', + path: item.path, + })); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + transformToConfig(payload: any, transformedPayload: AzionConfig) { + const storageConfig = payload.storage; + if (!Array.isArray(storageConfig) || storageConfig.length === 0) { + return; + } + + transformedPayload.storage = storageConfig.map((item) => ({ + name: item.name, + edgeAccess: item.edge_access || 'read_only', + path: item.path, + })); + + return transformedPayload.storage; + } +} + +export default StorageProcessConfigStrategy; diff --git a/packages/config/src/configProcessor/processStrategy/index.ts b/packages/config/src/configProcessor/processStrategy/index.ts index b3fc7d93..9297b867 100644 --- a/packages/config/src/configProcessor/processStrategy/index.ts +++ b/packages/config/src/configProcessor/processStrategy/index.ts @@ -9,6 +9,7 @@ import WafProcessConfigStrategy from '../processStrategy/implementations/secure/ import ProcessConfigContext from '../processStrategy/processConfigContext'; import FunctionsProcessConfigStrategy from './implementations/functionsProcessConfigStrategy'; import FirewallProcessConfigStrategy from './implementations/secure/firewallProcessConfigStrategy'; +import StorageProcessConfigStrategy from './implementations/storageProcessConfigStrategy'; function factoryProcessContext() { const processConfigContext = new ProcessConfigContext(); @@ -21,6 +22,7 @@ function factoryProcessContext() { processConfigContext.setStrategy('waf', new WafProcessConfigStrategy()); processConfigContext.setStrategy('firewall', new FirewallProcessConfigStrategy()); processConfigContext.setStrategy('functions', new FunctionsProcessConfigStrategy()); + processConfigContext.setStrategy('storage', new StorageProcessConfigStrategy()); // Rules must be last to apply to behaviors (origin, cache...) processConfigContext.setStrategy('rules', new RulesProcessConfigStrategy()); return processConfigContext; diff --git a/packages/config/src/types.ts b/packages/config/src/types.ts index 9095200c..9bc7a326 100644 --- a/packages/config/src/types.ts +++ b/packages/config/src/types.ts @@ -351,6 +351,26 @@ export type AzionFunction = { path: string; /** Optional arguments to be passed to the function */ args?: Record; + bindings?: { + storage?: Array<{ + /** Storage bucket name */ + bucket: string; + /** Storage prefix */ + prefix: string; + }>; + }; +}; + +/** + * Storage configuration for Azion. + */ +export type AzionStorage = { + /** Storage name */ + name: string; + /** Edge access type */ + edgeAccess?: 'read_only' | 'read_write' | 'restricted'; + /** Storage path */ + path: string; }; /** @@ -377,6 +397,8 @@ export type AzionConfig = { networkList?: AzionNetworkList[]; /** WAF configuration */ waf?: AzionWaf[]; + /** Storage configurations */ + storage?: AzionStorage[]; }; /** From 70338bbb108448e0e8cc66cfa99f32ea88f4b634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Narciso?= Date: Fri, 16 May 2025 10:57:43 -0300 Subject: [PATCH 2/8] test: fix config tests --- .../src/configProcessor/helpers/schema.ts | 12 ++++--- .../configProcessor/helpers/schemaManifest.ts | 8 ++--- .../processConfig/index.test.ts | 6 ---- .../storageProcessConfigStrategy.ts | 4 +-- .../validateConfig/index.test.ts | 3 -- packages/config/src/types.ts | 34 +++++++++++++------ 6 files changed, 37 insertions(+), 30 deletions(-) diff --git a/packages/config/src/configProcessor/helpers/schema.ts b/packages/config/src/configProcessor/helpers/schema.ts index 68ea80d1..a1acaa14 100644 --- a/packages/config/src/configProcessor/helpers/schema.ts +++ b/packages/config/src/configProcessor/helpers/schema.ts @@ -434,9 +434,9 @@ const schemaStorage = { pattern: '^.{6,63}$', errorMessage: "The 'name' field must be a string between 6 and 63 characters.", }, - path: { + dir: { type: 'string', - errorMessage: "The 'path' field must be a string.", + errorMessage: "The 'dir' field must be a string.", }, edgeAcess: { type: 'string', @@ -444,11 +444,11 @@ const schemaStorage = { errorMessage: "The 'edge_access' field must be one of: read_only, read_write, restricted.", }, }, - required: ['name', 'path'], + required: ['name', 'dir'], additionalProperties: false, errorMessage: { additionalProperties: 'No additional properties are allowed in storage items.', - required: "The 'name' and 'edge_access' fields are required.", + required: "The 'name' and 'dir' fields are required.", }, }; @@ -1050,7 +1050,11 @@ const azionConfigSchema = { }, }, }, + required: ['name'], additionalProperties: false, + errorMessage: { + required: "The 'name' field is required in the domain object.", + }, }, purge: { type: 'array', diff --git a/packages/config/src/configProcessor/helpers/schemaManifest.ts b/packages/config/src/configProcessor/helpers/schemaManifest.ts index e0fce696..bfb5a926 100644 --- a/packages/config/src/configProcessor/helpers/schemaManifest.ts +++ b/packages/config/src/configProcessor/helpers/schemaManifest.ts @@ -729,9 +729,9 @@ const schemaStorageManifest = { pattern: '^.{6,63}$', errorMessage: "The 'name' field must be a string between 6 and 63 characters.", }, - path: { + dir: { type: 'string', - errorMessage: "The 'path' field must be a string.", + errorMessage: "The 'dir' field must be a string.", }, edge_access: { type: 'string', @@ -739,11 +739,11 @@ const schemaStorageManifest = { errorMessage: "The 'edge_access' field must be one of: read_only, read_write, restricted.", }, }, - required: ['name', 'path'], + required: ['name', 'dir'], additionalProperties: false, errorMessage: { additionalProperties: 'No additional properties are allowed in storage items.', - required: "The 'name' field are required.", + required: "The 'name' and 'dir' fields are required.", }, }; diff --git a/packages/config/src/configProcessor/processConfig/index.test.ts b/packages/config/src/configProcessor/processConfig/index.test.ts index 8cc3a0e7..ac953c6a 100644 --- a/packages/config/src/configProcessor/processConfig/index.test.ts +++ b/packages/config/src/configProcessor/processConfig/index.test.ts @@ -9,9 +9,6 @@ describe('processConfig', () => { build: { preset: 'next', polyfills: true, - custom: { - minify: true, - }, }, }; expect(processConfig(config)).toEqual( @@ -19,9 +16,6 @@ describe('processConfig', () => { build: { preset: 'next', polyfills: true, - custom: { - minify: true, - }, }, }), ); diff --git a/packages/config/src/configProcessor/processStrategy/implementations/storageProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/storageProcessConfigStrategy.ts index 3ea5c1cd..e5c860ff 100644 --- a/packages/config/src/configProcessor/processStrategy/implementations/storageProcessConfigStrategy.ts +++ b/packages/config/src/configProcessor/processStrategy/implementations/storageProcessConfigStrategy.ts @@ -15,7 +15,7 @@ class StorageProcessConfigStrategy extends ProcessConfigStrategy { return config.storage.map((item) => ({ name: item.name, edge_access: item.edgeAccess || 'read_only', - path: item.path, + dir: item.dir, })); } @@ -29,7 +29,7 @@ class StorageProcessConfigStrategy extends ProcessConfigStrategy { transformedPayload.storage = storageConfig.map((item) => ({ name: item.name, edgeAccess: item.edge_access || 'read_only', - path: item.path, + dir: item.dir, })); return transformedPayload.storage; diff --git a/packages/config/src/configProcessor/validateConfig/index.test.ts b/packages/config/src/configProcessor/validateConfig/index.test.ts index b51259ec..814c72e6 100644 --- a/packages/config/src/configProcessor/validateConfig/index.test.ts +++ b/packages/config/src/configProcessor/validateConfig/index.test.ts @@ -8,9 +8,6 @@ describe('generate', () => { build: { preset: 'next', polyfills: true, - custom: { - minify: true, - }, }, }; expect(() => validateConfig(config)).not.toThrow(); diff --git a/packages/config/src/types.ts b/packages/config/src/types.ts index 9bc7a326..86a950bd 100644 --- a/packages/config/src/types.ts +++ b/packages/config/src/types.ts @@ -341,6 +341,24 @@ export type AzionNetworkList = { listContent: string[] | number[]; }; +/** + * Storage binding configuration for Azion. + */ +export type AzionStorageBinding = { + /** Storage bucket name */ + bucket: string; + /** Storage prefix */ + prefix: string; +}; + +/** + * Bindings configuration for Azion. + */ +export type AzionBindings = { + /** Storage bindings */ + storage?: AzionStorageBinding; +}; + /** * Function configuration for Azion. */ @@ -351,26 +369,20 @@ export type AzionFunction = { path: string; /** Optional arguments to be passed to the function */ args?: Record; - bindings?: { - storage?: Array<{ - /** Storage bucket name */ - bucket: string; - /** Storage prefix */ - prefix: string; - }>; - }; + /** Function bindings */ + bindings?: AzionBindings; }; /** * Storage configuration for Azion. */ -export type AzionStorage = { +export type AzionBucket = { /** Storage name */ name: string; /** Edge access type */ edgeAccess?: 'read_only' | 'read_write' | 'restricted'; /** Storage path */ - path: string; + dir: string; }; /** @@ -398,7 +410,7 @@ export type AzionConfig = { /** WAF configuration */ waf?: AzionWaf[]; /** Storage configurations */ - storage?: AzionStorage[]; + storage?: AzionBucket[]; }; /** From 9627b2bf224e63d9cc84e5380b5503b485ea7cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Narciso?= Date: Mon, 19 May 2025 11:05:13 -0300 Subject: [PATCH 3/8] fix: preset metadata schema --- packages/config/src/configProcessor/helpers/schema.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/config/src/configProcessor/helpers/schema.ts b/packages/config/src/configProcessor/helpers/schema.ts index a1acaa14..45e739ce 100644 --- a/packages/config/src/configProcessor/helpers/schema.ts +++ b/packages/config/src/configProcessor/helpers/schema.ts @@ -575,6 +575,10 @@ const azionConfigSchema = { type: 'string', errorMessage: "The 'ext' field in preset metadata must be a string", }, + registry: { + type: 'string', + errorMessage: "The 'registry' field in preset metadata must be a string", + }, }, required: ['name'], additionalProperties: false, From 7b94fa3149ce54b299701616035077bfd9c88a9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Narciso?= Date: Tue, 20 May 2025 10:06:03 -0300 Subject: [PATCH 4/8] fix: property name --- packages/config/src/configProcessor/helpers/schema.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/config/src/configProcessor/helpers/schema.ts b/packages/config/src/configProcessor/helpers/schema.ts index 45e739ce..95cdb18a 100644 --- a/packages/config/src/configProcessor/helpers/schema.ts +++ b/packages/config/src/configProcessor/helpers/schema.ts @@ -529,7 +529,7 @@ const azionConfigSchema = { bindings: { type: 'object', properties: { - edgeStorage: { + storage: { type: 'array', items: { type: 'object', @@ -546,11 +546,11 @@ const azionConfigSchema = { required: ['bucket'], additionalProperties: false, errorMessage: { - additionalProperties: 'No additional properties are allowed in edgeStorage items', + additionalProperties: 'No additional properties are allowed in storage items', required: "The 'bucket' field are required", }, }, - errorMessage: "The 'edgeStorage' field must be an array of storage bindings", + errorMessage: "The 'storage' field must be an array of storage bindings", }, }, additionalProperties: false, From 18d04ddd0ee029eaa241bac569eac0c92d8146d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Narciso?= Date: Tue, 20 May 2025 14:26:22 -0300 Subject: [PATCH 5/8] fix: rm build bindings --- .../src/configProcessor/helpers/schema.ts | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/packages/config/src/configProcessor/helpers/schema.ts b/packages/config/src/configProcessor/helpers/schema.ts index 95cdb18a..b5d8f77b 100644 --- a/packages/config/src/configProcessor/helpers/schema.ts +++ b/packages/config/src/configProcessor/helpers/schema.ts @@ -526,38 +526,6 @@ const azionConfigSchema = { enum: ['webpack', 'esbuild'], errorMessage: "The 'build.bundler' must be either 'webpack' or 'esbuild'", }, - bindings: { - type: 'object', - properties: { - storage: { - type: 'array', - items: { - type: 'object', - properties: { - bucket: { - type: 'string', - errorMessage: "The 'bucket' field must be a string", - }, - prefix: { - type: 'string', - errorMessage: "The 'prefix' field must be a string", - }, - }, - required: ['bucket'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in storage items', - required: "The 'bucket' field are required", - }, - }, - errorMessage: "The 'storage' field must be an array of storage bindings", - }, - }, - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in bindings object', - }, - }, preset: { anyOf: [ { type: 'string' }, From dc6f4a55e1f1cf5846f04c0ebd061227e2e73ec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Narciso?= Date: Tue, 20 May 2025 14:40:07 -0300 Subject: [PATCH 6/8] fix: function binding type --- .../src/configProcessor/helpers/schema.ts | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/packages/config/src/configProcessor/helpers/schema.ts b/packages/config/src/configProcessor/helpers/schema.ts index b5d8f77b..1f8bb730 100644 --- a/packages/config/src/configProcessor/helpers/schema.ts +++ b/packages/config/src/configProcessor/helpers/schema.ts @@ -471,32 +471,30 @@ const schemaFunction = { type: 'object', properties: { storage: { - type: 'array', - items: { - type: 'object', - properties: { - bucket: { - type: 'string', - errorMessage: "The 'bucket' field must be a string", - }, - prefix: { - type: 'string', - errorMessage: "The 'prefix' field must be a string", - }, + type: 'object', + properties: { + bucket: { + type: 'string', + errorMessage: "The 'bucket' field must be a string", }, - required: ['bucket'], - additionalProperties: false, - errorMessage: { - additionalProperties: 'No additional properties are allowed in storage items', - required: "The 'bucket' field is required", + prefix: { + type: 'string', + errorMessage: "The 'prefix' field must be a string", }, }, - errorMessage: "The 'storage' field must be an array of storage bindings", + required: ['bucket'], + additionalProperties: false, + errorMessage: { + type: "The 'storage' field must be an object", + additionalProperties: 'No additional properties are allowed in the storage object', + required: "The 'bucket' field is required in the storage object", + }, }, }, additionalProperties: false, errorMessage: { - additionalProperties: 'No additional properties are allowed in bindings object', + type: "The 'bindings' field must be an object", + additionalProperties: 'No additional properties are allowed in the bindings object', }, }, }, From 0f2fc16aaf19816d2d5bcb2102ed40a91b1f3f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Narciso?= Date: Tue, 20 May 2025 18:20:55 -0300 Subject: [PATCH 7/8] fix: typo --- packages/config/src/configProcessor/helpers/schema.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/config/src/configProcessor/helpers/schema.ts b/packages/config/src/configProcessor/helpers/schema.ts index 1f8bb730..f8d64fc4 100644 --- a/packages/config/src/configProcessor/helpers/schema.ts +++ b/packages/config/src/configProcessor/helpers/schema.ts @@ -438,7 +438,7 @@ const schemaStorage = { type: 'string', errorMessage: "The 'dir' field must be a string.", }, - edgeAcess: { + edgeAccess: { type: 'string', enum: ['read_only', 'read_write', 'restricted'], errorMessage: "The 'edge_access' field must be one of: read_only, read_write, restricted.", From 201854761fd1ebc730d5cbe1c064b39d775c8ab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Narciso?= Date: Tue, 20 May 2025 18:32:59 -0300 Subject: [PATCH 8/8] fix: validate bindings --- .../functionsProcessConfigStrategy.ts | 82 +++++++++++++------ .../configProcessor/processStrategy/index.ts | 2 +- 2 files changed, 56 insertions(+), 28 deletions(-) diff --git a/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts b/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts index 4c9525e2..16333bc1 100644 --- a/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts +++ b/packages/config/src/configProcessor/processStrategy/implementations/functionsProcessConfigStrategy.ts @@ -7,24 +7,41 @@ import ProcessConfigStrategy from '../processConfigStrategy'; * @description This class is implementation of the Functions ProcessConfig Strategy. */ class FunctionsProcessConfigStrategy extends ProcessConfigStrategy { + private validateStorageBinding(config: AzionConfig, bucketName: string, functionName: string) { + if (!Array.isArray(config?.storage) || !config.storage.find((storage) => storage.name === bucketName)) { + throw new Error( + `Function "${functionName}" references storage bucket "${bucketName}" which is not defined in the storage configuration.`, + ); + } + } + transformToManifest(config: AzionConfig) { if (!Array.isArray(config?.functions) || config?.functions.length === 0) { return; } - return config.functions.map((func) => ({ - name: func.name, - target: func.path, - args: func.args || {}, - bindings: func.bindings - ? { - edge_storage: func.bindings.storage?.map((storage) => ({ - bucket: storage.bucket, - prefix: storage.prefix, - })), - } - : undefined, - })); + return config.functions.map((func) => { + // Validar se o bucket referenciado existe + if (func.bindings?.storage?.bucket) { + this.validateStorageBinding(config, func.bindings.storage.bucket, func.name); + } + + return { + name: func.name, + target: func.path, + args: func.args || {}, + bindings: func.bindings + ? { + edge_storage: func.bindings.storage + ? { + bucket: func.bindings.storage.bucket, + prefix: func.bindings.storage.prefix, + } + : undefined, + } + : undefined, + }; + }); } // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -32,21 +49,32 @@ class FunctionsProcessConfigStrategy extends ProcessConfigStrategy { if (!Array.isArray(payload?.functions) || payload?.functions.length === 0) { return; } + // eslint-disable-next-line @typescript-eslint/no-explicit-any - transformedPayload.functions = payload.functions.map((func: any) => ({ - name: func.name, - path: func.target, - args: func.args || {}, - bindings: func.bindings - ? { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - storage: func.bindings.edge_storage?.map((storage: any) => ({ - bucket: storage.bucket, - prefix: storage.prefix, - })), - } - : undefined, - })); + transformedPayload.functions = payload.functions.map((func: any) => { + const config = { + name: func.name, + path: func.target, + args: func.args || {}, + bindings: func.bindings + ? { + storage: func.bindings.edge_storage + ? { + bucket: func.bindings.edge_storage.bucket, + prefix: func.bindings.edge_storage.prefix, + } + : undefined, + } + : undefined, + }; + + // Validar se o bucket referenciado existe + if (config.bindings?.storage?.bucket) { + this.validateStorageBinding(transformedPayload, config.bindings.storage.bucket, config.name); + } + + return config; + }); return transformedPayload.functions; } diff --git a/packages/config/src/configProcessor/processStrategy/index.ts b/packages/config/src/configProcessor/processStrategy/index.ts index 9297b867..d5f8fdca 100644 --- a/packages/config/src/configProcessor/processStrategy/index.ts +++ b/packages/config/src/configProcessor/processStrategy/index.ts @@ -20,9 +20,9 @@ function factoryProcessContext() { processConfigContext.setStrategy('purge', new PurgeProcessConfigStrategy()); processConfigContext.setStrategy('networkList', new NetworkListProcessConfigStrategy()); processConfigContext.setStrategy('waf', new WafProcessConfigStrategy()); + processConfigContext.setStrategy('storage', new StorageProcessConfigStrategy()); processConfigContext.setStrategy('firewall', new FirewallProcessConfigStrategy()); processConfigContext.setStrategy('functions', new FunctionsProcessConfigStrategy()); - processConfigContext.setStrategy('storage', new StorageProcessConfigStrategy()); // Rules must be last to apply to behaviors (origin, cache...) processConfigContext.setStrategy('rules', new RulesProcessConfigStrategy()); return processConfigContext;