From fc12bafdc352b6b2101097533750efb9d73d834e Mon Sep 17 00:00:00 2001 From: Nitish-Kulkarni3 Date: Thu, 25 Sep 2025 07:27:07 +0530 Subject: [PATCH] chore: adds support for app configuration as a destination Signed-off-by: nitish --- .secrets.baseline | 10 +- README.md | 91 +++++++++- event-notifications/v1.ts | 47 +++++ examples/event-notifications.v1.test.js | 157 +++++++++++++++++ package-lock.json | 8 +- .../event-notifications.v1.test.js | 162 +++++++++++++++++- 6 files changed, 464 insertions(+), 11 deletions(-) diff --git a/.secrets.baseline b/.secrets.baseline index 8488f5d..c96da4c 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,7 +3,7 @@ "files": "package-lock.json|^.secrets.baseline$", "lines": null }, - "generated_at": "2025-09-04T10:57:40Z", + "generated_at": "2025-09-25T01:54:39Z", "plugins_used": [ { "name": "AWSKeyDetector" @@ -126,7 +126,7 @@ "hashed_secret": "d4c3d66fd0c38547a3c7a4c6bdc29c36911bc030", "is_secret": false, "is_verified": false, - "line_number": 1601, + "line_number": 1658, "type": "Secret Keyword", "verified_result": null }, @@ -134,7 +134,7 @@ "hashed_secret": "3235b7f8d626cde63611d2e9ec43473f4e844c67", "is_secret": false, "is_verified": false, - "line_number": 2816, + "line_number": 2967, "type": "Base64 High Entropy String", "verified_result": null } @@ -144,7 +144,7 @@ "hashed_secret": "0cc20f91828bed53ddb6294968b7f9abd631a3ba", "is_secret": false, "is_verified": false, - "line_number": 1535, + "line_number": 1598, "type": "Secret Keyword", "verified_result": null }, @@ -152,7 +152,7 @@ "hashed_secret": "3235b7f8d626cde63611d2e9ec43473f4e844c67", "is_secret": false, "is_verified": false, - "line_number": 3175, + "line_number": 3332, "type": "Base64 High Entropy String", "verified_result": null } diff --git a/README.md b/README.md index 97d2785..a446faa 100644 --- a/README.md +++ b/README.md @@ -511,6 +511,7 @@ Currently, this functionality supports following destinations: 5. IBM Cloud Code Engine 6. IBM Cloud Object Storage 7. Webhook +8. App Configuration ```js const testDestinationParams = { @@ -731,6 +732,29 @@ For EventStreams template supported template type value: event_streams.notificat ``` For CodeEngine template supported template type value: ibmceapp.notification and ibmcejob.notification +#### AppConfiguration Template +```js + const templateConfigModel = { + body: , + }; + let createTemplateParams = { + instanceId: , + name: , + type: , + templateConfigModel, + description: , +}; + + let createTemplateResult; + try { + createTemplateResult = await eventNotificationsService.createTemplate(createTemplateParams); + console.log(JSON.stringify(createTemplateResult.result, null, 2)); + } catch (err) { + console.warn(err); + } +``` +For App Configuration template supported template type value: app_configuration.notification + ### List Templates ```js const params = { @@ -912,6 +936,30 @@ try { ``` For Event Streams template supported template type value: event_streams.notification +#### Update App Configuration Template +```js +const templateConfigModel = { + params: { + body: , + }, +}; +let replaceTemplateParams = { + instanceId: , + id: , + name: , + type: , + templateConfigModel, + description: , +}; +let replaceTemplateResult; +try { + replaceTemplateResult = await eventNotificationsService.replaceTemplate(replaceTemplateParams); +} catch (err) { + console.warn(err); +} +``` +For App Configuration template supported template type value: app_configuration.notification + #### Update CodeEngine Template ```js const templateConfigModel = { @@ -1008,7 +1056,6 @@ try { ### Create Subscription ```js -While Creating Subscription use any of one option from webhook or email // SubscriptionCreateAttributesWebhookAttributes const subscriptionCreateAttributesModel = { @@ -1034,6 +1081,46 @@ eventNotificationsService }); ``` +### ⚠️ Special Consideration for App Configuration Destination + +When creating or updating a subscription for an **App Configuration** destination, the `attributes` object has a specific rule: + +- You must include **either** `feature_flag_enabled` **or** `template_id_notification` +- You **cannot** include both properties together + +This ensures that a subscription is created for the correct use case — either **feature flag evaluation** or **notification templating**, but not both at once. + +#### ✅ Valid Example (Feature Flag Evaluation) + +```js +{ + "attributes": { + "feature_flag_enabled": true + } +} +``` + +#### ✅ Valid Example (template association) + +```js +{ + "attributes": { + "template_id_notification": "" + } +} +``` + +#### ❌ Invalid Example (Not Allowed) + +```js +{ + "attributes": { + "feature_flag_enabled": true, + "template_id_notification": "" + } +} +``` + ### List Subscriptions ```js @@ -1586,6 +1673,8 @@ Find [event_notifications_v1.env.hide](https://github.com/IBM/event-notification - `EVENT_NOTIFICATIONS_EVENT_STREAMS_ENDPOINT` - Event streams end point - `EVENT_NOTIFICATIONS_CODE_ENGINE_APP_TEMPLATE_BODY` - base 64 encoded json body for Code Engine Application - `EVENT_NOTIFICATIONS_CODE_ENGINE_JOB_TEMPLATE_BODY` - base 64 encoded json body for Code Engine Job +- `EVENT_NOTIFICATIONS_APP_CONFIG_CRN` - CRN of App Configuration instance +- `EVENT_NOTIFICATIONS_APP_CONFIG_TEMPLATE_BODY` - base 64 encoded json body for App Configuration ## Questions diff --git a/event-notifications/v1.ts b/event-notifications/v1.ts index 8f63f2c..edc718c 100644 --- a/event-notifications/v1.ts +++ b/event-notifications/v1.ts @@ -3684,6 +3684,7 @@ namespace EventNotificationsV1 { SMTP_CUSTOM = 'smtp_custom', SMS_CUSTOM = 'sms_custom', EVENT_STREAMS = 'event_streams', + APP_CONFIGURATION = 'app_configuration', } } @@ -5099,6 +5100,19 @@ namespace EventNotificationsV1 { verification: string; } + /** Payload describing a App Configuration destination configuration. */ + export interface DestinationConfigOneOfAppConfigurationDestinationConfig + extends DestinationConfigOneOf { + /** The App Configuration Destination type. */ + type: string; + /** CRN of the App Configuration instance. */ + crn: string; + /** Environment ID of App Configuration. */ + environment_id: string; + /** Feature ID of App Configuration. */ + feature_id: string; + } + /** Payload describing a Chrome destination configuration. */ export interface DestinationConfigOneOfChromeDestinationConfig extends DestinationConfigOneOf { /** FCM api_key. */ @@ -5293,6 +5307,15 @@ namespace EventNotificationsV1 { sensitive_headers?: string[]; } + /** The attributes for a App Configuration notification. */ + export interface SubscriptionAttributesAppConfigurationAttributesResponse + extends SubscriptionAttributes { + /** App Configuration enable feature flag attribute. */ + feature_flag_enabled?: boolean; + /** App Configuration template id. */ + template_id_notification?: string; + } + /** The attributes for a Code Engine response. */ export interface SubscriptionAttributesCodeEngineAttributesResponse extends SubscriptionAttributes { @@ -5412,6 +5435,15 @@ namespace EventNotificationsV1 { add_notification_payload: boolean; } + /** The attributes for a App Configuration subscription. */ + export interface SubscriptionCreateAttributesAppConfigurationAttributes + extends SubscriptionCreateAttributes { + /** App Configuration enable feature flag attribute. */ + feature_flag_enabled?: boolean; + /** App Configuration template id. */ + template_id_notification?: string; + } + /** The attributes for a Code Engine subscription. */ export interface SubscriptionCreateAttributesCodeEngineAttributes extends SubscriptionCreateAttributes { @@ -5521,6 +5553,15 @@ namespace EventNotificationsV1 { template_id_notification?: string; } + /** The attributes for a App Configuration subscription. */ + export interface SubscriptionUpdateAttributesAppConfigurationAttributes + extends SubscriptionUpdateAttributes { + /** App Configuration enable feature flag attribute. */ + feature_flag_enabled?: boolean; + /** App Configuration template id. */ + template_id_notification?: string; + } + /** The attributes for a Code Engine subscription. */ export interface SubscriptionUpdateAttributesCodeEngineAttributes extends SubscriptionUpdateAttributes { @@ -5644,6 +5685,12 @@ namespace EventNotificationsV1 { template_id_notification?: string; } + /** Payload describing a App Configuration template configuration. */ + export interface TemplateConfigOneOfAppConfigurationTemplateConfig extends TemplateConfigOneOf { + /** Template body(Base64 encoded). */ + body: string; + } + /** Payload describing a code engine application template configuration. */ export interface TemplateConfigOneOfCodeEngineApplicationTemplateConfig extends TemplateConfigOneOf { diff --git a/examples/event-notifications.v1.test.js b/examples/event-notifications.v1.test.js index 896e4b6..7188ea8 100644 --- a/examples/event-notifications.v1.test.js +++ b/examples/event-notifications.v1.test.js @@ -68,6 +68,7 @@ let destinationId17 = ''; let destinationId18 = ''; let destinationId19 = ''; let destinationId20 = ''; +let destinationId22 = ''; let subscriptionId = ''; let subscriptionId1 = ''; let subscriptionId2 = ''; @@ -79,6 +80,7 @@ let subscriptionId7 = ''; let subscriptionId8 = ''; let subscriptionId9 = ''; let subscriptionId10 = ''; +const subscriptionId22 = ''; let fcmServerKey = ''; let fcmSenderId = ''; let safariCertificatePath = ''; @@ -119,6 +121,9 @@ let eventStreamsTemplateBody = ''; let eventStreamsCRN = ''; let eventStreamsTopic = ''; let eventStreamsEndPoint = ''; +let appConfigTemplateBody = ''; +let appConfigCRN = ''; +let appConfigTemplateID = ''; // Save original console.log const originalLog = console.log; @@ -163,6 +168,8 @@ describe('EventNotificationsV1', () => { eventStreamsCRN = config.eventStreamsCrn; eventStreamsTopic = config.eventStreamsTopic; eventStreamsEndPoint = config.eventStreamsEndpoint; + appConfigTemplateBody = config.appConfigTemplateBody; + appConfigCRN = config.appConfigCrn; let eventNotificationsService = EventNotificationsV1.newInstance({}); @@ -1100,6 +1107,36 @@ describe('EventNotificationsV1', () => { } catch (err) { console.warn(err); } + + const destinationAppConfigParamsModel = { + crn: appConfigCRN, + type: 'features', + environment_id: 'dev', + feature_id: 'flag_test', + }; + + const destinationAppConfigModel = { + params: destinationAppConfigParamsModel, + }; + + name = 'app_config_destination'; + description = 'app_config Destination'; + type = 'app_configuration'; + params = { + instanceId, + name, + type, + description, + config: destinationAppConfigModel, + }; + + try { + const appConfigRes = await eventNotificationsService.createDestination(params); + console.log(JSON.stringify(appConfigRes.result, null, 2)); + destinationId22 = appConfigRes.result.id; + } catch (err) { + console.warn(err); + } // end-create_destination }); @@ -1314,6 +1351,29 @@ describe('EventNotificationsV1', () => { } catch (err) { console.warn(err); } + + const appConfigTemplateConfigModel = { + body: appConfigTemplateBody, + }; + + name = 'App Config template name'; + description = 'App Config template description'; + type = 'app_configuration.notification'; + createTemplateParams = { + instanceId, + name, + type, + params: appConfigTemplateConfigModel, + description, + }; + + try { + createTemplateResult = await eventNotificationsService.createTemplate(createTemplateParams); + console.log(JSON.stringify(createTemplateResult.result, null, 2)); + appConfigTemplateID = createTemplateResult.result.id; + } catch (err) { + console.warn(err); + } // end-create_template }); @@ -1935,6 +1995,35 @@ describe('EventNotificationsV1', () => { } catch (err) { console.warn(err); } + + const destAppConfigParamsModel = { + crn: appConfigCRN, + type: 'features', + environment_id: 'dev', + feature_id: 'flag_test', + }; + + const destinationAppConfigParamsModel = { + params: destAppConfigParamsModel, + }; + + name = 'App Config updated'; + description = 'This destination is for App config'; + + params = { + instanceId, + id: destinationId22, + name, + description, + config: destinationAppConfigParamsModel, + }; + + try { + const appConfigRes = await eventNotificationsService.updateDestination(params); + console.log(JSON.stringify(appConfigRes.result, null, 2)); + } catch (err) { + console.warn(err); + } // end-update_destination }); @@ -2094,6 +2183,31 @@ describe('EventNotificationsV1', () => { } catch (err) { console.warn(err); } + + const appConfigJobTemplateConfigModel = { + body: appConfigTemplateBody, + }; + + name = 'app config template name update'; + description = 'app config template description update'; + type = 'app_configuration.notification'; + replaceTemplateParams = { + instanceId, + id: appConfigTemplateID, + name, + type, + params: appConfigJobTemplateConfigModel, + description, + }; + + try { + replaceTemplateResult = + await eventNotificationsService.replaceTemplate(replaceTemplateParams); + console.log(JSON.stringify(replaceTemplateResult.result, null, 2)); + appConfigTemplateID = replaceTemplateResult.result.id; + } catch (err) { + console.warn(err); + } // end-update_template }); @@ -2374,6 +2488,27 @@ describe('EventNotificationsV1', () => { } catch (err) { console.warn(err); } + + name = 'App Configuration subscription'; + description = 'Subscription for App Configuration'; + params = { + instanceId, + name, + destinationId: destinationId22, + topicId, + description, + attributes: { + feature_flag_enabled: true, + }, + }; + + try { + const appConfigRes = await eventNotificationsService.createSubscription(params); + console.log(JSON.stringify(appConfigRes.result, null, 2)); + subscriptionId10 = appConfigRes.result.id; + } catch (err) { + console.warn(err); + } // end-create_subscription }); @@ -2757,6 +2892,25 @@ describe('EventNotificationsV1', () => { } catch (err) { console.warn(err); } + + name = 'App configuration subscription update'; + description = 'Subscription for the App configuration update'; + params = { + instanceId, + name, + id: subscriptionId22, + description, + attributes: { + template_id_notification: appConfigTemplateID, + }, + }; + + try { + const appConfigRes = await eventNotificationsService.updateSubscription(params); + console.log(JSON.stringify(appConfigRes.result, null, 2)); + } catch (err) { + console.warn(err); + } // end-update_subscription }); @@ -3381,6 +3535,7 @@ describe('EventNotificationsV1', () => { subscriptionId8, subscriptionId9, subscriptionId10, + subscriptionId22, ]; for (let i = 0; i < subscriptions.length; i += 1) { @@ -3465,6 +3620,7 @@ describe('EventNotificationsV1', () => { destinationId18, destinationId19, destinationId20, + destinationId22, ]; for (let i = 0; i < destinations.length; i += 1) { @@ -3498,6 +3654,7 @@ describe('EventNotificationsV1', () => { webhookTemplateID, pagerdutyTemplateID, eventStreamsTemplateID, + appConfigTemplateID, ]; for (let i = 0; i < templates.length; i += 1) { diff --git a/package-lock.json b/package-lock.json index 4acc297..4e01dae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,7 @@ "typescript": "^5.4.5" }, "engines": { - "node": ">=16.0.0" + "node": ">=18.0.0" } }, "node_modules/@ampproject/remapping": { @@ -3060,9 +3060,9 @@ } }, "node_modules/axios": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", - "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", diff --git a/test/integration/event-notifications.v1.test.js b/test/integration/event-notifications.v1.test.js index 5c5f601..0b0bf26 100644 --- a/test/integration/event-notifications.v1.test.js +++ b/test/integration/event-notifications.v1.test.js @@ -56,6 +56,7 @@ let destinationId17 = ''; let destinationId18 = ''; let destinationId19 = ''; let destinationId20 = ''; +let destinationId22 = ''; let subscriptionId = ''; let subscriptionId1 = ''; @@ -76,6 +77,7 @@ let subscriptionId17 = ''; let subscriptionId18 = ''; let subscriptionId19 = ''; let subscriptionId20 = ''; +let subscriptionId22 = ''; let safariCertificatePath = ''; let integrationId = ''; let sNowClientId = ''; @@ -123,6 +125,9 @@ let codeEngineApplicationTemplateID = ''; let codeEngineJobTemplateID = ''; let codeEngineAppTemplateBody = ''; let codeEngineJobTemplateBody = ''; +let appConfigTemplateBody = ''; +let appConfigCRN = ''; +let appConfigTemplateID = ''; describe('EventNotificationsV1_integration', () => { jest.setTimeout(timeout); @@ -171,7 +176,8 @@ describe('EventNotificationsV1_integration', () => { eventStreamsEndPoint = config.eventStreamsEndpoint; codeEngineAppTemplateBody = config.codeEngineAppTemplateBody; codeEngineJobTemplateBody = config.codeEngineJobTemplateBody; - + appConfigTemplateBody = config.appConfigTemplateBody; + appConfigCRN = config.appConfigCrn; eventNotificationsService.enableRetries(); }); @@ -1074,6 +1080,38 @@ describe('EventNotificationsV1_integration', () => { expect(eventStreamsRes.result.description).toBe(description); destinationId20 = eventStreamsRes.result.id; + const destinationAppConfigParamsModel = { + crn: appConfigCRN, + type: 'features', + environment_id: 'dev', + feature_id: 'flag_test', + }; + + const destinationAppConfigModel = { + params: destinationAppConfigParamsModel, + }; + + name = 'app_config_destination'; + description = 'app_config Destination'; + type = 'app_configuration'; + params = { + instanceId, + name, + type, + description, + config: destinationAppConfigModel, + }; + + const appConfigRes = await eventNotificationsService.createDestination(params); + expect(appConfigRes).toBeDefined(); + expect(appConfigRes.status).toBe(201); + expect(appConfigRes.result).toBeDefined(); + + expect(appConfigRes.result.type).toBe(type); + expect(appConfigRes.result.name).toBe(name); + expect(appConfigRes.result.description).toBe(description); + destinationId22 = appConfigRes.result.id; + // // The following status codes aren't covered by tests. // Please provide integration tests for these too. @@ -1325,6 +1363,31 @@ describe('EventNotificationsV1_integration', () => { expect(createTemplateResult.result.name).toBe(name); expect(createTemplateResult.result.description).toBe(description); codeEngineJobTemplateID = createTemplateResult.result.id; + + const appConfigTemplateConfigModel = { + body: appConfigTemplateBody, + }; + + name = 'App Config template name'; + description = 'App Config template description'; + type = 'app_configuration.notification'; + createTemplateParams = { + instanceId, + name, + type, + params: appConfigTemplateConfigModel, + description, + }; + + createTemplateResult = await eventNotificationsService.createTemplate(createTemplateParams); + expect(createTemplateResult).toBeDefined(); + expect(createTemplateResult.status).toBe(201); + expect(createTemplateResult.result).toBeDefined(); + + expect(createTemplateResult.result.type).toBe(type); + expect(createTemplateResult.result.name).toBe(name); + expect(createTemplateResult.result.description).toBe(description); + appConfigTemplateID = createTemplateResult.result.id; }); test('listDestinations()', async () => { @@ -1886,6 +1949,35 @@ describe('EventNotificationsV1_integration', () => { expect(eventStreamsRes.result).toBeDefined(); expect(eventStreamsRes.result.name).toBe(name); expect(eventStreamsRes.result.description).toBe(description); + + const destAppConfigParamsModel = { + crn: appConfigCRN, + type: 'features', + environment_id: 'dev', + feature_id: 'flag_test', + }; + + const destinationAppConfigParamsModel = { + params: destAppConfigParamsModel, + }; + + name = 'App Config updated'; + description = 'This destination is for App config'; + + params = { + instanceId, + id: destinationId22, + name, + description, + config: destinationAppConfigParamsModel, + }; + + const appConfigRes = await eventNotificationsService.updateDestination(params); + expect(appConfigRes).toBeDefined(); + expect(appConfigRes.status).toBe(200); + expect(appConfigRes.result).toBeDefined(); + expect(appConfigRes.result.name).toBe(name); + expect(appConfigRes.result.description).toBe(description); // // The following status codes aren't covered by tests. // Please provide integration tests for these too. @@ -2096,6 +2188,31 @@ describe('EventNotificationsV1_integration', () => { expect(replaceTemplateResult.result.type).toBe(type); expect(replaceTemplateResult.result.name).toBe(name); expect(replaceTemplateResult.result.description).toBe(description); + + const appConfigTemplateConfigModel = { + body: appConfigTemplateBody, + }; + + name = 'app config template name update'; + description = 'app config template description update'; + type = 'app_configuration.notification'; + replaceTemplateParams = { + instanceId, + id: appConfigTemplateID, + name, + type, + params: appConfigTemplateConfigModel, + description, + }; + + replaceTemplateResult = await eventNotificationsService.replaceTemplate(replaceTemplateParams); + expect(replaceTemplateResult).toBeDefined(); + expect(replaceTemplateResult.status).toBe(200); + expect(replaceTemplateResult.result).toBeDefined(); + + expect(replaceTemplateResult.result.type).toBe(type); + expect(replaceTemplateResult.result.name).toBe(name); + expect(replaceTemplateResult.result.description).toBe(description); }); test('createSubscription()', async () => { @@ -2529,6 +2646,27 @@ describe('EventNotificationsV1_integration', () => { expect(eventStreamsRes.result.description).toBe(description); subscriptionId20 = eventStreamsRes.result.id; + name = 'App Configuration subscription'; + description = 'Subscription for App Configuration'; + params = { + instanceId, + name, + destinationId: destinationId22, + topicId, + description, + attributes: { + feature_flag_enabled: true, + }, + }; + + const appConfigRes = await eventNotificationsService.createSubscription(params); + expect(appConfigRes).toBeDefined(); + expect(appConfigRes.status).toBe(201); + expect(appConfigRes.result).toBeDefined(); + expect(appConfigRes.result.name).toBe(name); + expect(appConfigRes.result.description).toBe(description); + subscriptionId22 = appConfigRes.result.id; + // // The following status codes aren't covered by tests. // Please provide integration tests for these too. @@ -3047,6 +3185,25 @@ describe('EventNotificationsV1_integration', () => { expect(eventStreamsRes.result).toBeDefined(); expect(eventStreamsRes.result.name).toBe(name); expect(eventStreamsRes.result.description).toBe(description); + + name = 'App configuration subscription update'; + description = 'Subscription for the App configuration update'; + params = { + instanceId, + name, + id: subscriptionId22, + description, + attributes: { + template_id_notification: appConfigTemplateID, + }, + }; + + const appConfigRes = await eventNotificationsService.updateSubscription(params); + expect(appConfigRes).toBeDefined(); + expect(appConfigRes.status).toBe(200); + expect(appConfigRes.result).toBeDefined(); + expect(appConfigRes.result.name).toBe(name); + expect(appConfigRes.result.description).toBe(description); // The following status codes aren't covered by tests. // Please provide integration tests for these too. // @@ -3554,6 +3711,7 @@ describe('EventNotificationsV1_integration', () => { subscriptionId18, subscriptionId19, subscriptionId20, + subscriptionId22, ]; for (let i = 0; i < subscriptions.length; i += 1) { @@ -3646,6 +3804,7 @@ describe('EventNotificationsV1_integration', () => { destinationId18, destinationId19, destinationId20, + destinationId22, ]; for (let i = 0; i < destinations.length; i += 1) { @@ -3680,6 +3839,7 @@ describe('EventNotificationsV1_integration', () => { eventStreamsTemplateID, codeEngineApplicationTemplateID, codeEngineJobTemplateID, + appConfigTemplateID, ]; for (let i = 0; i < templates.length; i += 1) {