diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index fa6428b319..0163f5d42f 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -2355,15 +2355,15 @@ Test flows are JSON files that define a workflow using the Appmixer flow format. "component-id-1": { "type": "appmixer.utils.controls.OnStart", "x": 100, - "y": 200, + "y": 192, "source": {}, "version": "1.0.0", "config": {} }, "component-id-2": { "type": "appmixer.connector.core.ComponentName", - "x": 300, - "y": 200, + "x": 250, + "y": 192, "version": "1.0.0", "source": { "in": { @@ -2838,16 +2838,16 @@ The `result` property MUST use `{{{uuid}}}` pattern referencing `$.after-all.out 4. **Multiple Assert Components - Separate Branches** - **CRITICAL**: If a flow has more than one Assert component, they MUST be in separate branches - Each Assert should test a different aspect or operation - - Branches should have different y-coordinates for visual separation + - Branches should have different y-coordinates for visual separation (increments of ~160: 192, 352, 512, etc.) - All Assert components feed into the AfterAll component to merge results - Example structure: ``` - Component A (y=100) - ├─> Assert 1 (y=100) ─┐ - └─> Component B (y=300) ─> Assert 2 (y=300) ─┘ - └─> AfterAll + Component A (y=192) + ├─> Assert 1 (y=192) ─┐ + └─> Component B (y=352) ─> Assert 2 (y=352) ─┘ + └─> AfterAll (y=192) ``` - - See `test-flow-images.json` for reference implementation + - See `test-flow-campaign-crud.json` and `test-flow-contact-crud.json` for reference implementations 5. **Field Name Accuracy** - Use EXACT field names from component.json @@ -2864,10 +2864,21 @@ The `result` property MUST use `{{{uuid}}}` pattern referencing `$.after-all.out - Use AfterAll to ensure cleanup runs after all assertions - Connect cleanup components properly -8. **Component Coordinates** - - Use x/y coordinates for visual layout - - Space components horizontally (200-300px apart) - - Arrange vertically for parallel branches (especially for multiple Assert components) +8. **Component Layout and Coordinates** + - **Main flow baseline**: Use `y: 192` as the main horizontal line for the primary flow path + - **X-axis spacing**: Space components approximately 150 units apart horizontally + - **Parallel branches**: When operations branch from a single component: + - The main branch stays at `y: 192` + - Each parallel branch increments Y by approximately 160 units (e.g., `y: 352`, `y: 512`, etc.) + - **Assert alignment**: Each Assert component should have the **same Y coordinate** as the component it validates + - **Convergence**: AfterAll, cleanup components, and ProcessE2EResults return to the main line at `y: 192` + - **Example layout for parallel branches**: + ``` + CreateItem (x:400, y:192) ─────────> Assert-create (x:976, y:192) ──┐ + ├─> GetItem (x:608, y:352) ─────> Assert-get (x:976, y:352) ─────┤─> AfterAll (y:192) + └─> UpdateItem (x:800, y:512) ──> Assert-update (x:976, y:512) ──┘ + ``` + - **Typical X coordinates progression**: `~100` (OnStart) → `~250` (SetVariable) → `~400` (Create) → `~600-800` (operations) → `~976` (Asserts) → `~1184+` (AfterAll, cleanup, results) 9. **Naming Conventions** - Use descriptive component IDs: `create-document`, `assert-content-exists` @@ -2884,15 +2895,15 @@ The `result` property MUST use `{{{uuid}}}` pattern referencing `$.after-all.out "start": { "type": "appmixer.utils.controls.OnStart", "x": 100, - "y": 200, + "y": 192, "source": {}, "version": "1.0.0", "config": {} }, "create-item": { "type": "appmixer.service.core.CreateItem", - "x": 300, - "y": 200, + "x": 250, + "y": 192, "version": "1.0.0", "source": { "in": { @@ -2919,8 +2930,8 @@ The `result` property MUST use `{{{uuid}}}` pattern referencing `$.after-all.out }, "get-item": { "type": "appmixer.service.core.GetItem", - "x": 500, - "y": 200, + "x": 400, + "y": 192, "version": "1.0.0", "source": { "in": { @@ -2952,8 +2963,8 @@ The `result` property MUST use `{{{uuid}}}` pattern referencing `$.after-all.out }, "assert-item": { "type": "appmixer.utils.test.Assert", - "x": 700, - "y": 200, + "x": 550, + "y": 192, "version": "1.0.0", "source": { "in": { @@ -2993,8 +3004,8 @@ The `result` property MUST use `{{{uuid}}}` pattern referencing `$.after-all.out }, "after-all": { "type": "appmixer.utils.test.AfterAll", - "x": 900, - "y": 200, + "x": 700, + "y": 192, "version": "1.0.0", "source": { "in": { @@ -3009,8 +3020,8 @@ The `result` property MUST use `{{{uuid}}}` pattern referencing `$.after-all.out }, "delete-item": { "type": "appmixer.service.core.DeleteItem", - "x": 1100, - "y": 200, + "x": 850, + "y": 192, "version": "1.0.0", "source": { "in": { @@ -3042,8 +3053,8 @@ The `result` property MUST use `{{{uuid}}}` pattern referencing `$.after-all.out }, "process-results": { "type": "appmixer.utils.test.ProcessE2EResults", - "x": 1300, - "y": 200, + "x": 1000, + "y": 192, "version": "1.0.0", "source": { "in": { diff --git a/src/appmixer/getResponse/artifacts/test-flows/test-flow-campaign-crud.json b/src/appmixer/getResponse/artifacts/test-flows/test-flow-campaign-crud.json new file mode 100644 index 0000000000..f56fe1cc48 --- /dev/null +++ b/src/appmixer/getResponse/artifacts/test-flows/test-flow-campaign-crud.json @@ -0,0 +1,442 @@ +{ + "flow": { + "start": { + "type": "appmixer.utils.controls.OnStart", + "x": 94, + "y": 192, + "source": {}, + "version": "1.0.0", + "config": {} + }, + "set-variables": { + "type": "appmixer.utils.controls.SetVariable", + "x": 240, + "y": 192, + "source": { + "in": { + "start": [ + "out" + ] + } + }, + "version": "1.0.0", + "config": { + "transform": { + "in": { + "start": { + "out": { + "type": "json2new", + "modifiers": { + "variables": { + "b2b3da79-4d91-4796-950b-8bef1112d9e7": { + "functions": [ + { + "name": "g_timestamp" + } + ] + }, + "f7266361-4795-48ab-8b8a-de5be7f55b6a": { + "functions": [ + { + "name": "g_timestamp" + } + ] + } + } + }, + "lambda": { + "variables": { + "ADD": [ + { + "type": "text", + "name": "campaignName", + "toggle": false, + "text": "E2E Test Campaign{{{b2b3da79-4d91-4796-950b-8bef1112d9e7}}}" + }, + { + "type": "text", + "name": "updatedCampaignName", + "text": "E2E Test Campaign Updated{{{f7266361-4795-48ab-8b8a-de5be7f55b6a}}}" + } + ] + } + } + } + } + } + } + } + }, + "before-all": { + "type": "appmixer.utils.test.BeforeAll", + "x": 394, + "y": 192, + "source": { + "in": { + "set-variables": [ + "out" + ] + } + }, + "version": "1.0.0" + }, + "create-campaign": { + "type": "appmixer.getResponse.core.CreateCampaign", + "x": 528, + "y": 192, + "version": "1.0.0", + "source": { + "in": { + "before-all": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "before-all": { + "out": { + "type": "json2new", + "modifiers": { + "name": { + "var-campaign-name": { + "variable": "$.set-variables.out.campaignName", + "functions": [] + } + } + }, + "lambda": { + "name": "{{{var-campaign-name}}}" + } + } + } + } + } + } + }, + "assert-create": { + "type": "appmixer.utils.test.Assert", + "x": 944, + "y": 192, + "version": "1.0.0", + "source": { + "in": { + "create-campaign": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "create-campaign": { + "out": { + "type": "json2new", + "modifiers": { + "expression": { + "campaign-id-check": { + "variable": "$.create-campaign.out.campaignId", + "functions": [] + }, + "campaign-name-check": { + "variable": "$.create-campaign.out.name", + "functions": [] + }, + "57a16a41-26c9-4597-a0ba-b9241eb457ab": { + "variable": "$.set-variables.out.campaignName", + "functions": [] + } + } + }, + "lambda": { + "expression": { + "AND": [ + { + "assertion": "notEmpty", + "field": "{{{campaign-id-check}}}" + }, + { + "assertion": "equal", + "field": "{{{campaign-name-check}}}", + "expected": "{{{57a16a41-26c9-4597-a0ba-b9241eb457ab}}}" + } + ] + } + } + } + } + } + } + } + }, + "list-campaigns": { + "type": "appmixer.getResponse.core.FindCampaigns", + "x": 736, + "y": 528, + "version": "1.0.0", + "source": { + "in": { + "create-campaign": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "create-campaign": { + "out": { + "type": "json2new", + "modifiers": { + "outputType": {}, + "status": {} + }, + "lambda": { + "outputType": "first", + "status": "active" + } + } + } + } + } + } + }, + "assert-list": { + "type": "appmixer.utils.test.Assert", + "x": 944, + "y": 512, + "version": "1.0.0", + "config": { + "transform": { + "in": { + "list-campaigns": { + "out": { + "type": "json2new", + "modifiers": { + "expression": { + "campaigns-count-check": { + "variable": "$.list-campaigns.out.count", + "functions": [] + }, + "b4aed32f-e63f-4812-9d3d-1aca33a1d27b": { + "variable": "$.create-campaign.out.campaignId", + "functions": [] + } + } + }, + "lambda": { + "expression": { + "AND": [ + { + "assertion": "notEmpty", + "field": "{{{campaigns-count-check}}}" + }, + { + "field": "{{{b4aed32f-e63f-4812-9d3d-1aca33a1d27b}}}", + "assertion": "notEmpty" + } + ] + } + } + } + } + } + } + }, + "source": { + "in": { + "list-campaigns": [ + "out" + ] + } + } + }, + "update-campaign": { + "type": "appmixer.getResponse.core.UpdateCampaign", + "x": 736, + "y": 368, + "version": "1.0.0", + "source": { + "in": { + "create-campaign": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "create-campaign": { + "out": { + "type": "json2new", + "modifiers": { + "campaignId": { + "var-campaign-id": { + "variable": "$.create-campaign.out.campaignId", + "functions": [] + } + }, + "name": { + "var-updated-name": { + "variable": "$.set-variables.out.updatedCampaignName", + "functions": [] + } + } + }, + "lambda": { + "campaignId": "{{{var-campaign-id}}}", + "name": "{{{var-updated-name}}}" + } + } + } + } + } + } + }, + "assert-update": { + "type": "appmixer.utils.test.Assert", + "x": 944, + "y": 368, + "version": "1.0.0", + "source": { + "in": { + "update-campaign": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "update-campaign": { + "out": { + "type": "json2new", + "modifiers": { + "expression": { + "success-check": { + "variable": "$.set-variables.out.updatedCampaignName", + "functions": [] + } + } + }, + "lambda": { + "expression": { + "AND": [ + { + "assertion": "notEmpty", + "field": "{{{success-check}}}" + } + ] + } + } + } + } + } + } + } + }, + "after-all": { + "type": "appmixer.utils.test.AfterAll", + "x": 1306, + "y": 192, + "source": { + "in": { + "assert-create": [ + "out" + ], + "assert-list": [ + "out" + ], + "assert-update": [ + "out" + ] + } + }, + "version": "1.0.0", + "config": { + "properties": { + "timeout": 30 + } + } + }, + "delete-campaign": { + "type": "appmixer.getResponse.core.DeleteCampaign", + "x": 1456, + "y": 192, + "version": "1.0.0", + "source": { + "in": { + "after-all": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "after-all": { + "out": { + "type": "json2new", + "modifiers": { + "campaignId": { + "var-campaign-id": { + "variable": "$.create-campaign.out.campaignId", + "functions": [] + } + } + }, + "lambda": { + "campaignId": "{{{var-campaign-id}}}" + } + } + } + } + } + } + }, + "process-results": { + "type": "appmixer.utils.test.ProcessE2EResults", + "x": 1606, + "y": 192, + "version": "1.0.1", + "source": { + "in": { + "delete-campaign": [ + "out" + ] + } + }, + "config": { + "properties": {}, + "transform": { + "in": { + "delete-campaign": { + "out": { + "type": "json2new", + "modifiers": { + "recipients": {}, + "testCase": {}, + "result": { + "result-var": { + "variable": "$.after-all.out", + "functions": [] + } + } + }, + "lambda": { + "recipients": "test@appmixer.ai", + "testCase": "E2E GetResponse - campaign-crud", + "result": "{{{result-var}}}" + } + } + } + } + } + } + } + }, + "name": "E2E GetResponse - campaign-crud", + "type": "automation", + "notes": {} +} \ No newline at end of file diff --git a/src/appmixer/getResponse/artifacts/test-flows/test-flow-contact-crud.json b/src/appmixer/getResponse/artifacts/test-flows/test-flow-contact-crud.json new file mode 100644 index 0000000000..e85deba245 --- /dev/null +++ b/src/appmixer/getResponse/artifacts/test-flows/test-flow-contact-crud.json @@ -0,0 +1,525 @@ +{ + "flow": { + "on-start": { + "type": "appmixer.utils.controls.OnStart", + "x": 100, + "y": 192, + "source": {}, + "version": "1.0.0", + "config": {} + }, + "set-variables": { + "type": "appmixer.utils.controls.SetVariable", + "x": 250, + "y": 192, + "source": { + "in": { + "on-start": [ + "out" + ] + } + }, + "version": "1.0.0", + "config": { + "transform": { + "in": { + "on-start": { + "out": { + "type": "json2new", + "modifiers": { + "variables": { + "3c223411-6849-4f7d-b353-0ad8e061fc0f": { + "functions": [ + { + "name": "g_timestamp" + } + ] + }, + "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6": { + "functions": [ + { + "name": "g_timestamp" + } + ] + } + } + }, + "lambda": { + "variables": { + "ADD": [ + { + "type": "text", + "name": "testEmail", + "toggle": false, + "text": "test-findcontacts@example.com" + }, + { + "type": "text", + "name": "testName", + "text": "Test FindContacts" + }, + { + "type": "text", + "name": "campaignName", + "toggle": false, + "text": "E2E Test Campaign {{{a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6}}}" + }, + { + "type": "text", + "name": "updatedName", + "toggle": false, + "text": "E2E {{{3c223411-6849-4f7d-b353-0ad8e061fc0f}}} Test Contact Updated" + } + ] + } + } + } + } + } + } + } + }, + "create-campaign": { + "type": "appmixer.getResponse.core.CreateCampaign", + "x": 400, + "y": 192, + "version": "1.0.0", + "source": { + "in": { + "set-variables": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "set-variables": { + "out": { + "type": "json2new", + "modifiers": { + "name": { + "name-var": { + "variable": "$.set-variables.out.campaignName", + "functions": [] + } + } + }, + "lambda": { + "name": "{{{name-var}}}" + } + } + } + } + } + } + }, + "create-contact": { + "type": "appmixer.getResponse.core.CreateContact", + "x": 550, + "y": 192, + "version": "1.0.0", + "source": { + "in": { + "create-campaign": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "create-campaign": { + "out": { + "type": "json2new", + "modifiers": { + "email": { + "email-var": { + "variable": "$.set-variables.out.testEmail", + "functions": [] + } + }, + "name": { + "name-var": { + "variable": "$.set-variables.out.testName", + "functions": [] + } + }, + "campaignId": { + "campaign-var": { + "variable": "$.create-campaign.out.campaignId", + "functions": [] + } + } + }, + "lambda": { + "email": "{{{email-var}}}", + "name": "{{{name-var}}}", + "campaignId": "{{{campaign-var}}}" + } + } + } + } + } + } + }, + "list-contacts": { + "type": "appmixer.getResponse.core.FindContacts", + "x": 700, + "y": 192, + "version": "1.0.0", + "source": { + "in": { + "create-contact": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "create-contact": { + "out": { + "lambda": { + "email": "{{{email-var}}}", + "outputType": "first" + }, + "modifiers": { + "email": { + "email-var": { + "variable": "$.set-variables.out.testEmail", + "functions": [] + } + }, + "outputType": {} + } + } + } + } + } + } + }, + "assert-create": { + "type": "appmixer.utils.test.Assert", + "x": 1276, + "y": 192, + "version": "1.0.0", + "config": { + "transform": {} + } + }, + "get-contact": { + "type": "appmixer.getResponse.core.GetContact", + "x": 908, + "y": 352, + "version": "1.0.0", + "config": { + "transform": { + "in": { + "list-contacts": { + "out": { + "lambda": { + "contactId": "{{{id-var}}}" + }, + "modifiers": { + "contactId": { + "id-var": { + "variable": "$.list-contacts.out.contactId", + "functions": [] + } + } + } + } + } + } + } + }, + "source": { + "in": { + "list-contacts": [ + "out" + ] + } + } + }, + "assert-get": { + "type": "appmixer.utils.test.Assert", + "x": 1276, + "y": 352, + "version": "1.0.0", + "source": { + "in": { + "get-contact": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "get-contact": { + "out": { + "type": "json2new", + "modifiers": { + "expression": { + "email-check": { + "variable": "$.get-contact.out.email", + "functions": [] + }, + "name-check": { + "variable": "$.get-contact.out.name", + "functions": [] + } + } + }, + "lambda": { + "expression": { + "AND": [ + { + "field": "{{{email-check}}}", + "assertion": "notEmpty" + }, + { + "field": "{{{name-check}}}", + "assertion": "notEmpty" + } + ] + } + } + } + } + } + } + } + }, + "update-contact": { + "type": "appmixer.getResponse.core.UpdateContact", + "x": 1100, + "y": 512, + "version": "1.0.0", + "source": { + "in": { + "get-contact": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "get-contact": { + "out": { + "type": "json2new", + "modifiers": { + "contactId": { + "id-var": { + "variable": "$.list-contacts.out.contactId", + "functions": [] + } + }, + "name": { + "updated-name-var": { + "variable": "$.set-variables.out.updatedName", + "functions": [] + } + } + }, + "lambda": { + "contactId": "{{{id-var}}}", + "name": "{{{updated-name-var}}}" + } + } + } + } + } + } + }, + "assert-update": { + "type": "appmixer.utils.test.Assert", + "x": 1276, + "y": 512, + "version": "1.0.0", + "source": { + "in": { + "update-contact": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "update-contact": { + "out": { + "type": "json2new", + "modifiers": { + "expression": { + "success-check": { + "variable": "$.set-variables.out.updatedName", + "functions": [] + } + } + }, + "lambda": { + "expression": { + "AND": [ + { + "field": "{{{success-check}}}", + "assertion": "notEmpty" + } + ] + } + } + } + } + } + } + } + }, + "after-all": { + "type": "appmixer.utils.test.AfterAll", + "x": 1484, + "y": 192, + "version": "1.0.0", + "source": { + "in": { + "assert-create": [ + "out" + ], + "assert-get": [ + "out" + ], + "assert-update": [ + "out" + ] + } + }, + "config": { + "properties": { + "timeout": 30 + } + } + }, + "delete-contact": { + "type": "appmixer.getResponse.core.DeleteContact", + "x": 1618, + "y": 192, + "version": "1.0.0", + "source": { + "in": { + "after-all": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "after-all": { + "out": { + "type": "json2new", + "modifiers": { + "contactId": { + "id-var": { + "variable": "$.list-contacts.out.contactId", + "functions": [] + } + } + }, + "lambda": { + "contactId": "{{{id-var}}}" + } + } + } + } + } + } + }, + "delete-campaign": { + "type": "appmixer.getResponse.core.DeleteCampaign", + "x": 1768, + "y": 192, + "version": "1.0.0", + "source": { + "in": { + "delete-contact": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "delete-contact": { + "out": { + "type": "json2new", + "modifiers": { + "campaignId": { + "campaign-id-var": { + "variable": "$.create-campaign.out.campaignId", + "functions": [] + } + } + }, + "lambda": { + "campaignId": "{{{campaign-id-var}}}" + } + } + } + } + } + } + }, + "process-results": { + "type": "appmixer.utils.test.ProcessE2EResults", + "x": 1918, + "y": 192, + "version": "1.0.1", + "source": { + "in": { + "delete-campaign": [ + "out" + ] + } + }, + "config": { + "properties": {}, + "transform": { + "in": { + "delete-campaign": { + "out": { + "type": "json2new", + "modifiers": { + "recipients": {}, + "testCase": {}, + "result": { + "result-var": { + "variable": "$.after-all.out", + "functions": [] + } + } + }, + "lambda": { + "recipients": "test@appmixer.ai", + "testCase": "E2E GetResponse - contact-crud", + "result": "{{{result-var}}}" + } + } + } + } + } + } + } + }, + "name": "E2E GetResponse - contact-crud", + "type": "automation", + "notes": { + "37d69a71-d851-4c2b-bed4-a6b896c05cde": { + "x": 560, + "y": -48, + "width": 240, + "height": 194, + "content": "Create Contact - API is delayed - when you create a new contact, it won't be listed straight away" + } + } +} \ No newline at end of file diff --git a/src/appmixer/getResponse/artifacts/test-flows/test-flow-list-operations.json b/src/appmixer/getResponse/artifacts/test-flows/test-flow-list-operations.json new file mode 100644 index 0000000000..6d7c8a00c5 --- /dev/null +++ b/src/appmixer/getResponse/artifacts/test-flows/test-flow-list-operations.json @@ -0,0 +1,313 @@ +{ + "flow": { + "on-start": { + "type": "appmixer.utils.controls.OnStart", + "x": 100, + "y": 200, + "source": {}, + "version": "1.0.0", + "config": {} + }, + "before-all": { + "type": "appmixer.utils.test.BeforeAll", + "x": 250, + "y": 200, + "source": { + "in": { + "on-start": [ + "out" + ] + } + }, + "version": "1.0.0", + "config": {} + }, + "list-contacts": { + "type": "appmixer.getResponse.core.ListContacts", + "x": 400, + "y": 100, + "version": "1.0.0", + "source": { + "in": { + "before-all": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "before-all": { + "out": { + "type": "json2new", + "modifiers": { + "state": {}, + "outputType": {} + }, + "lambda": { + "state": "active", + "outputType": "array" + } + } + } + } + } + } + }, + "list-campaigns": { + "type": "appmixer.getResponse.core.ListCampaigns", + "x": 400, + "y": 200, + "version": "1.0.0", + "source": { + "in": { + "before-all": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "before-all": { + "out": { + "type": "json2new", + "modifiers": { + "status": {}, + "outputType": {} + }, + "lambda": { + "status": "active", + "outputType": "array" + } + } + } + } + } + } + }, + "list-tags": { + "type": "appmixer.getResponse.core.ListTags", + "x": 400, + "y": 300, + "version": "1.0.0", + "source": { + "in": { + "before-all": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "before-all": { + "out": { + "type": "json2new", + "modifiers": { + "outputType": {} + }, + "lambda": { + "outputType": "array" + } + } + } + } + } + } + }, + "assert-contacts": { + "type": "appmixer.utils.test.Assert", + "x": 600, + "y": 100, + "version": "1.0.0", + "source": { + "in": { + "list-contacts": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "list-contacts": { + "out": { + "type": "json2new", + "modifiers": { + "expression": { + "contacts-check": { + "variable": "$.list-contacts.out.result", + "functions": [] + } + } + }, + "lambda": { + "expression": { + "AND": [ + { + "field": "{{{contacts-check}}}", + "assertion": "notEmpty" + } + ] + } + } + } + } + } + } + } + }, + "assert-campaigns": { + "type": "appmixer.utils.test.Assert", + "x": 600, + "y": 200, + "version": "1.0.0", + "source": { + "in": { + "list-campaigns": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "list-campaigns": { + "out": { + "type": "json2new", + "modifiers": { + "expression": { + "campaigns-check": { + "variable": "$.list-campaigns.out", + "functions": [] + } + } + }, + "lambda": { + "expression": { + "AND": [ + { + "field": "{{{campaigns-check}}}", + "assertion": "notEmpty" + } + ] + } + } + } + } + } + } + } + }, + "assert-tags": { + "type": "appmixer.utils.test.Assert", + "x": 600, + "y": 300, + "version": "1.0.0", + "source": { + "in": { + "list-tags": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "list-tags": { + "out": { + "type": "json2new", + "modifiers": { + "expression": { + "tags-check": { + "variable": "$.list-tags.out", + "functions": [] + } + } + }, + "lambda": { + "expression": { + "AND": [ + { + "field": "{{{tags-check}}}", + "assertion": "notEmpty" + } + ] + } + } + } + } + } + } + } + }, + "after-all": { + "type": "appmixer.utils.test.AfterAll", + "x": 800, + "y": 200, + "version": "1.0.0", + "source": { + "in": { + "assert-contacts": [ + "out" + ], + "assert-campaigns": [ + "out" + ], + "assert-tags": [ + "out" + ] + } + }, + "config": { + "properties": { + "timeout": 60 + } + } + }, + "process-results": { + "type": "appmixer.utils.test.ProcessE2EResults", + "x": 950, + "y": 200, + "version": "1.0.1", + "source": { + "in": { + "after-all": [ + "out" + ] + } + }, + "config": { + "properties": {}, + "transform": { + "in": { + "after-all": { + "out": { + "type": "json2new", + "modifiers": { + "recipients": {}, + "testCase": {}, + "result": { + "result-var": { + "variable": "$.after-all.out", + "functions": [] + } + } + }, + "lambda": { + "recipients": "test@appmixer.ai", + "testCase": "E2E GetResponse - list-operations", + "result": "{{{result-var}}}" + } + } + } + } + } + } + } + }, + "name": "E2E GetResponse - list-operations", + "type": "automation", + "notes": {} +} \ No newline at end of file diff --git a/src/appmixer/getResponse/artifacts/test-flows/test-flow-tag-crud.json b/src/appmixer/getResponse/artifacts/test-flows/test-flow-tag-crud.json new file mode 100644 index 0000000000..98c7b3be44 --- /dev/null +++ b/src/appmixer/getResponse/artifacts/test-flows/test-flow-tag-crud.json @@ -0,0 +1,428 @@ +{ + "flow": { + "on-start": { + "type": "appmixer.utils.controls.OnStart", + "x": 94, + "y": 208, + "source": {}, + "version": "1.0.0", + "config": {} + }, + "set-variables": { + "type": "appmixer.utils.controls.SetVariable", + "x": 244, + "y": 208, + "source": { + "in": { + "on-start": [ + "out" + ] + } + }, + "version": "1.0.0", + "config": { + "transform": { + "in": { + "on-start": { + "out": { + "type": "json2new", + "modifiers": { + "variables": {} + }, + "lambda": { + "variables": { + "ADD": [ + { + "type": "text", + "name": "tagName", + "toggle": false, + "text": "E2ETestTag" + }, + { + "type": "text", + "name": "updatedTagName", + "text": "E2ETestTagUpdated" + } + ] + } + } + } + } + } + } + } + }, + "before-all": { + "type": "appmixer.utils.test.BeforeAll", + "x": 394, + "y": 208, + "source": { + "in": { + "set-variables": [ + "out" + ] + } + }, + "version": "1.0.0", + "config": {} + }, + "create-tag": { + "type": "appmixer.getResponse.core.CreateTag", + "x": 544, + "y": 208, + "version": "1.0.0", + "source": { + "in": { + "before-all": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "before-all": { + "out": { + "type": "json2new", + "modifiers": { + "name": { + "5b67e399-7e91-4af6-9076-f2e15a143b3f": { + "variable": "$.set-variables.out.tagName", + "functions": [] + } + } + }, + "lambda": { + "name": "{{{5b67e399-7e91-4af6-9076-f2e15a143b3f}}}" + } + } + } + } + } + } + }, + "assert-create": { + "type": "appmixer.utils.test.Assert", + "x": 928, + "y": 208, + "version": "1.0.0", + "source": { + "in": { + "create-tag": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "create-tag": { + "out": { + "type": "json2new", + "modifiers": { + "expression": { + "tag-id-check": { + "variable": "$.create-tag.out.tagId", + "functions": [] + }, + "tag-name-check": { + "variable": "$.create-tag.out.name", + "functions": [] + }, + "115e1f5c-2fd4-45a0-8b5d-5a1f30a2bd71": { + "variable": "$.set-variables.out.tagName", + "functions": [] + } + } + }, + "lambda": { + "expression": { + "AND": [ + { + "assertion": "notEmpty", + "field": "{{{tag-id-check}}}" + }, + { + "assertion": "equal", + "field": "{{{tag-name-check}}}", + "expected": "{{{115e1f5c-2fd4-45a0-8b5d-5a1f30a2bd71}}}" + } + ] + } + } + } + } + } + } + } + }, + "list-tags": { + "type": "appmixer.getResponse.core.ListTags", + "x": 752, + "y": 560, + "version": "1.0.0", + "source": { + "in": { + "create-tag": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "create-tag": { + "out": { + "type": "json2new", + "modifiers": { + "queryName": {}, + "outputType": {} + }, + "lambda": { + "queryName": "E2E Test Tag", + "outputType": "array" + } + } + } + } + } + } + }, + "assert-list": { + "type": "appmixer.utils.test.Assert", + "x": 928, + "y": 560, + "version": "1.0.0", + "source": { + "in": { + "list-tags": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "list-tags": { + "out": { + "type": "json2new", + "modifiers": { + "expression": { + "result-check": { + "variable": "$.list-tags.out.result", + "functions": [] + }, + "9947f62e-31ed-48a8-a35e-a54f5a16aea8": { + "variable": "$.list-tags.out.count", + "functions": [] + } + } + }, + "lambda": { + "expression": { + "AND": [ + { + "assertion": "notEmpty", + "field": "{{{result-check}}}" + }, + { + "field": "{{{9947f62e-31ed-48a8-a35e-a54f5a16aea8}}}", + "assertion": "notEmpty" + } + ] + } + } + } + } + } + } + } + }, + "update-tag": { + "type": "appmixer.getResponse.core.UpdateTag", + "x": 752, + "y": 416, + "version": "1.0.0", + "source": { + "in": { + "create-tag": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "create-tag": { + "out": { + "type": "json2new", + "modifiers": { + "tagId": { + "tag-id-var": { + "variable": "$.create-tag.out.tagId", + "functions": [] + } + }, + "name": { + "3bd5144f-ed83-44a5-a9bd-c48cadb91d80": { + "variable": "$.set-variables.out.updatedTagName", + "functions": [] + } + } + }, + "lambda": { + "tagId": "{{{tag-id-var}}}", + "name": "{{{3bd5144f-ed83-44a5-a9bd-c48cadb91d80}}}" + } + } + } + } + } + } + }, + "assert-update": { + "type": "appmixer.utils.test.Assert", + "x": 928, + "y": 416, + "version": "1.0.0", + "source": { + "in": { + "update-tag": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "update-tag": { + "out": { + "type": "json2new", + "modifiers": { + "expression": { + "success-check": { + "variable": "$.set-variables.out.updatedTagName", + "functions": [] + } + } + }, + "lambda": { + "expression": { + "AND": [ + { + "assertion": "notEmpty", + "field": "{{{success-check}}}" + } + ] + } + } + } + } + } + } + } + }, + "after-all": { + "type": "appmixer.utils.test.AfterAll", + "x": 1136, + "y": 208, + "version": "1.0.0", + "source": { + "in": { + "assert-create": [ + "out" + ], + "assert-list": [ + "out" + ], + "assert-update": [ + "out" + ] + } + }, + "config": { + "properties": { + "timeout": 30 + } + } + }, + "delete-tag": { + "type": "appmixer.getResponse.core.DeleteTag", + "x": 1270, + "y": 208, + "version": "1.0.0", + "source": { + "in": { + "after-all": [ + "out" + ] + } + }, + "config": { + "transform": { + "in": { + "after-all": { + "out": { + "type": "json2new", + "modifiers": { + "tagId": { + "tag-id-var": { + "variable": "$.create-tag.out.tagId", + "functions": [] + } + } + }, + "lambda": { + "tagId": "{{{tag-id-var}}}" + } + } + } + } + } + } + }, + "process-results": { + "type": "appmixer.utils.test.ProcessE2EResults", + "x": 1420, + "y": 208, + "version": "1.0.1", + "source": { + "in": { + "delete-tag": [ + "out" + ] + } + }, + "config": { + "properties": {}, + "transform": { + "in": { + "delete-tag": { + "out": { + "type": "json2new", + "modifiers": { + "recipients": {}, + "testCase": {}, + "result": { + "result-var": { + "variable": "$.after-all.out", + "functions": [] + } + } + }, + "lambda": { + "recipients": "test@appmixer.ai", + "testCase": "E2E GetResponse - tag-crud", + "result": "{{{result-var}}}" + } + } + } + } + } + } + } + }, + "name": "E2E GetResponse - tag-crud", + "type": "automation", + "notes": {} +} \ No newline at end of file diff --git a/src/appmixer/getResponse/auth.js b/src/appmixer/getResponse/auth.js new file mode 100644 index 0000000000..c003c39772 --- /dev/null +++ b/src/appmixer/getResponse/auth.js @@ -0,0 +1,39 @@ +'use strict'; + +module.exports = { + type: 'apiKey', + definition: { + auth: { + apiKey: { + type: 'text', + name: 'API Key', + tooltip: 'Enter your getResponse API Key. You can find it in your getResponse account under Tools > Integrations & API > API.' + } + }, + + requestProfileInfo(context) { + const apiKey = context.apiKey; + return { + key: apiKey.substr(0, 3) + '...' + apiKey.substr(4) + }; + }, + accountNameFromProfileInfo: 'key', + + validate: async (context) => { + // getResponse API: https://api.getresponse.com/v3/accounts + const response = await context.httpRequest({ + method: 'GET', + url: 'https://api.getresponse.com/v3/accounts', + headers: { + 'X-Auth-Token': `api-key ${context.apiKey}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + }); + if (!response.data || !response.data.accountId) { + throw new Error('Authentication failed: Could not retrieve account info.'); + } + return true; + } + } +}; diff --git a/src/appmixer/getResponse/bundle.json b/src/appmixer/getResponse/bundle.json new file mode 100644 index 0000000000..7388bf8ee2 --- /dev/null +++ b/src/appmixer/getResponse/bundle.json @@ -0,0 +1,9 @@ +{ + "name": "appmixer.getResponse", + "version": "1.0.0", + "changelog": { + "1.0.0": [ + "Initial version" + ] + } +} \ No newline at end of file diff --git a/src/appmixer/getResponse/core/CreateCampaign/CreateCampaign.js b/src/appmixer/getResponse/core/CreateCampaign/CreateCampaign.js new file mode 100644 index 0000000000..6de4e9addc --- /dev/null +++ b/src/appmixer/getResponse/core/CreateCampaign/CreateCampaign.js @@ -0,0 +1,68 @@ +'use strict'; + +module.exports = { + async receive(context) { + const { + name, + languageCode, + isDefault, + confirmationSubject, + confirmationFromName, + confirmationFromEmail, + confirmationReplyToEmail, + confirmationRedirectUrl + } = context.messages.in.content; + + if (!name) { + throw new context.CancelError('Campaign Name is required!'); + } + + const body = { + name + }; + + if (languageCode) { + body.languageCode = languageCode; + } + + if (isDefault !== undefined) { + body.isDefault = isDefault; + } + + // Build confirmation object if any confirmation fields are provided + if (confirmationSubject || confirmationFromName || confirmationFromEmail) { + body.confirmation = { + fromField: {}, + subscriptionConfirmationSubject: confirmationSubject || 'Please confirm your subscription' + }; + + if (confirmationFromName) { + body.confirmation.fromField.fromFieldName = confirmationFromName; + } + if (confirmationFromEmail) { + body.confirmation.fromField.fromFieldId = confirmationFromEmail; + } + if (confirmationReplyToEmail) { + body.confirmation.replyTo = { + fromFieldId: confirmationReplyToEmail + }; + } + if (confirmationRedirectUrl) { + body.confirmation.redirectUrl = confirmationRedirectUrl; + } + } + + const response = await context.httpRequest({ + method: 'POST', + url: 'https://api.getresponse.com/v3/campaigns', + headers: { + 'X-Auth-Token': `api-key ${context.auth.apiKey}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + data: body + }); + + return context.sendJson(response.data, 'out'); + } +}; diff --git a/src/appmixer/getResponse/core/CreateCampaign/component.json b/src/appmixer/getResponse/core/CreateCampaign/component.json new file mode 100644 index 0000000000..f926257641 --- /dev/null +++ b/src/appmixer/getResponse/core/CreateCampaign/component.json @@ -0,0 +1,139 @@ +{ + "name": "appmixer.getResponse.core.CreateCampaign", + "label": "Create Campaign", + "author": "Appmixer ", + "description": "Create a new campaign.", + "version": "1.0.0", + "private": false, + "auth": { + "service": "appmixer:getResponse" + }, + "quota": { + "manager": "appmixer:getResponse", + "resources": "requests" + }, + "inPorts": [ + { + "name": "in", + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "languageCode": { + "type": "string" + }, + "isDefault": { + "type": "boolean" + }, + "confirmationSubject": { + "type": "string" + }, + "confirmationFromName": { + "type": "string" + }, + "confirmationFromEmail": { + "type": "string" + }, + "confirmationReplyToEmail": { + "type": "string" + }, + "confirmationRedirectUrl": { + "type": "string" + } + }, + "required": [ + "name" + ] + }, + "inspector": { + "inputs": { + "name": { + "type": "text", + "label": "Campaign Name", + "index": 0, + "tooltip": "The name of the campaign" + }, + "languageCode": { + "type": "text", + "label": "Language Code", + "index": 1, + "tooltip": "Language code (e.g., EN, DE, PL)" + }, + "isDefault": { + "type": "toggle", + "label": "Is Default", + "index": 2, + "tooltip": "Set this campaign as the default campaign" + }, + "confirmationSubject": { + "type": "text", + "label": "Confirmation Subject", + "index": 3, + "tooltip": "Subject line for the subscription confirmation email" + }, + "confirmationFromName": { + "type": "text", + "label": "Confirmation From Name", + "index": 4, + "tooltip": "Sender name that will appear in the confirmation email" + }, + "confirmationFromEmail": { + "type": "text", + "label": "Confirmation From Email", + "index": 5, + "tooltip": "Sender email address for the confirmation email" + }, + "confirmationReplyToEmail": { + "type": "text", + "label": "Confirmation Reply-To Email", + "index": 6, + "tooltip": "Reply-to email address for the confirmation email" + }, + "confirmationRedirectUrl": { + "type": "text", + "label": "Confirmation Redirect URL", + "index": 7, + "tooltip": "URL to redirect the user to after they confirm their subscription" + } + } + } + } + ], + "outPorts": [ + { + "name": "out", + "schema": { + "type": "object", + "properties": { + "campaignId": { + "type": "string", + "title": "Campaign ID" + }, + "name": { + "type": "string", + "title": "Name" + }, + "languageCode": { + "type": "string", + "title": "Language Code" + }, + "isDefault": { + "type": "string", + "title": "Is Default" + }, + "createdOn": { + "type": "string", + "title": "Created On" + }, + "href": { + "type": "string", + "title": "URL" + } + } + } + } + ], + "icon": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI3LjguMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA4Ny45IDU2IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA4Ny45IDU2OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzAwQUJFOTt9Cgkuc3Qxe2ZpbGw6I0ZGRkZGRjt9Cjwvc3R5bGU+CjxnPgoJPGc+CgkJPHBhdGggY2xhc3M9InN0MCIgZD0iTTg3LjgsNTIuMWMwLDIuMS0xLjcsMy43LTMuNywzLjdIMy45Yy0yLjEsMC0zLjctMS43LTMuNy0zLjdWMy45YzAtMi4xLDEuNy0zLjcsMy43LTMuN0g4NAoJCQljMi4xLDAsMy43LDEuNywzLjcsMy43VjUyLjF6Ii8+Cgk8L2c+Cgk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODYuOSwxLjVDNzYuNiwxNy43LDYwLjUsMzIuNCw0My45LDMyLjFjLTYuMS0wLjEtMTMuMS0xLjktMTktNi43Yy00LjQtMy42LTguOC05LjItMTAuNS0xNy4yCgkJYy0wLjcsMC0xLjIsMC0xLjgsMGMtMi43LDAtNC4zLDIuNC00LDQuNmMwLjEsMC41LDAuMiwxLjEsMC4zLDEuN2MxLjMsNy44LDUuOSwxNi42LDEzLjYsMjIuM2M2LDQuNCwxMy40LDcuMywyMS41LDcuMwoJCWMxNC42LDAsMzAuMi05LjMsNDMuOC0zMy4yVjMuOUM4Ny44LDMsODcuNSwyLjIsODYuOSwxLjV6Ii8+CjwvZz4KPC9zdmc+Cg==" +} \ No newline at end of file diff --git a/src/appmixer/getResponse/core/CreateContact/CreateContact.js b/src/appmixer/getResponse/core/CreateContact/CreateContact.js new file mode 100644 index 0000000000..33a8fe99ae --- /dev/null +++ b/src/appmixer/getResponse/core/CreateContact/CreateContact.js @@ -0,0 +1,97 @@ +'use strict'; + +module.exports = { + async receive(context) { + const { + email, + name, + campaignId, + dayOfCycle, + ipAddress, + note, + scoring, + customFieldId, + customFieldValue, + tagId + } = context.messages.in.content; + + // Validate required inputs + if (!email) { + throw new context.CancelError('Email is required!'); + } + + if (!campaignId) { + throw new context.CancelError('Campaign ID is required!'); + } + + // Build request body + const body = { + email, + campaign: { + campaignId + } + }; + + // Add optional fields if provided + if (name) { + body.name = name; + } + + if (dayOfCycle !== undefined && dayOfCycle !== null) { + body.dayOfCycle = dayOfCycle; + } + + if (ipAddress) { + body.ipAddress = ipAddress; + } + + if (note) { + body.note = note; + } + + if (scoring !== undefined && scoring !== null) { + body.scoring = scoring; + } + + if (customFieldId && customFieldValue) { + body.customFieldValues = [ + { + customFieldId, + value: Array.isArray(customFieldValue) ? customFieldValue : [customFieldValue] + } + ]; + } + + if (tagId) { + body.tags = [ + { + tagId + } + ]; + } + + // https://apireference.getresponse.com/#contacts + // Note: The API returns 202 Accepted with no body, only a Location header + await context.httpRequest({ + method: 'POST', + url: 'https://api.getresponse.com/v3/contacts', + headers: { + 'X-Auth-Token': `api-key ${context.auth.apiKey}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + data: body + }); + + // The API returns 202 Accepted with no body. + // Echo back the input values for reference. + // To get the contactId, use ListContacts with email filter after this component. + const result = { + email, + name: name || null, + campaignId + }; + + return context.sendJson(result, 'out'); + } +}; diff --git a/src/appmixer/getResponse/core/CreateContact/component.json b/src/appmixer/getResponse/core/CreateContact/component.json new file mode 100644 index 0000000000..d05b080ad8 --- /dev/null +++ b/src/appmixer/getResponse/core/CreateContact/component.json @@ -0,0 +1,156 @@ +{ + "name": "appmixer.getResponse.core.CreateContact", + "label": "Create Contact", + "author": "Appmixer ", + "description": "Create a new contact in a specified campaign (list). Note: The API returns 202 Accepted with no body. To get the created contact ID, use List Contacts with the email filter after this component.", + "version": "1.0.0", + "private": false, + "auth": { + "service": "appmixer:getResponse" + }, + "quota": { + "manager": "appmixer:getResponse", + "resources": "requests" + }, + "inPorts": [ + { + "name": "in", + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email" + }, + "name": { + "type": "string" + }, + "campaignId": { + "type": "string" + }, + "dayOfCycle": { + "type": "integer" + }, + "ipAddress": { + "type": "string" + }, + "note": { + "type": "string" + }, + "scoring": { + "type": "integer" + }, + "customFieldId": { + "type": "string" + }, + "customFieldValue": { + "type": "array", + "items": { + "type": "string" + } + }, + "tagId": { + "type": "string" + } + }, + "required": [ + "email", + "campaignId" + ] + }, + "inspector": { + "inputs": { + "email": { + "type": "text", + "tooltip": "Contact email address.", + "label": "Email", + "index": 0 + }, + "name": { + "type": "text", + "tooltip": "Contact name.", + "label": "Name", + "index": 1 + }, + "campaignId": { + "type": "text", + "tooltip": "ID of the target campaign.", + "label": "Campaign ID", + "index": 2 + }, + "dayOfCycle": { + "type": "number", + "tooltip": "Day of cycle for autoresponders. 0 means today.", + "label": "Day of Cycle", + "index": 3 + }, + "ipAddress": { + "type": "text", + "tooltip": "IP address of the contact (optional, for compliance/geo info).", + "label": "IP Address", + "index": 4 + }, + "note": { + "type": "text", + "tooltip": "Optional note about the contact.", + "label": "Note", + "index": 5 + }, + "scoring": { + "type": "number", + "tooltip": "Contact scoring value.", + "label": "Scoring", + "index": 6 + }, + "customFieldId": { + "type": "text", + "tooltip": "Custom field ID.", + "label": "Custom Field ID", + "index": 7 + }, + "customFieldValue": { + "type": "expression", + "tooltip": "Values for the custom field (string array).", + "label": "Custom Field Value", + "fields": { + "customFieldValue_item": { + "type": "text", + "label": "Item" + } + }, + "index": 8 + }, + "tagId": { + "type": "text", + "tooltip": "Tag ID to assign.", + "label": "Tag ID", + "index": 9 + } + } + } + } + ], + "outPorts": [ + { + "name": "out", + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "title": "Email" + }, + "name": { + "type": "string", + "title": "Name" + }, + "campaignId": { + "type": "string", + "title": "Campaign ID" + } + } + } + } + ], + "icon": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI3LjguMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA4Ny45IDU2IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA4Ny45IDU2OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzAwQUJFOTt9Cgkuc3Qxe2ZpbGw6I0ZGRkZGRjt9Cjwvc3R5bGU+CjxnPgoJPGc+CgkJPHBhdGggY2xhc3M9InN0MCIgZD0iTTg3LjgsNTIuMWMwLDIuMS0xLjcsMy43LTMuNywzLjdIMy45Yy0yLjEsMC0zLjctMS43LTMuNy0zLjdWMy45YzAtMi4xLDEuNy0zLjcsMy43LTMuN0g4NAoJCQljMi4xLDAsMy43LDEuNywzLjcsMy43VjUyLjF6Ii8+Cgk8L2c+Cgk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODYuOSwxLjVDNzYuNiwxNy43LDYwLjUsMzIuNCw0My45LDMyLjFjLTYuMS0wLjEtMTMuMS0xLjktMTktNi43Yy00LjQtMy42LTguOC05LjItMTAuNS0xNy4yCgkJYy0wLjcsMC0xLjIsMC0xLjgsMGMtMi43LDAtNC4zLDIuNC00LDQuNmMwLjEsMC41LDAuMiwxLjEsMC4zLDEuN2MxLjMsNy44LDUuOSwxNi42LDEzLjYsMjIuM2M2LDQuNCwxMy40LDcuMywyMS41LDcuMwoJCWMxNC42LDAsMzAuMi05LjMsNDMuOC0zMy4yVjMuOUM4Ny44LDMsODcuNSwyLjIsODYuOSwxLjV6Ii8+CjwvZz4KPC9zdmc+Cg==" +} diff --git a/src/appmixer/getResponse/core/CreateTag/CreateTag.js b/src/appmixer/getResponse/core/CreateTag/CreateTag.js new file mode 100644 index 0000000000..815d8b0156 --- /dev/null +++ b/src/appmixer/getResponse/core/CreateTag/CreateTag.js @@ -0,0 +1,33 @@ +'use strict'; + +module.exports = { + async receive(context) { + const { name } = context.messages.in.content; + + if (!name) { + throw new context.CancelError('Tag Name is required!'); + } + + const response = await context.httpRequest({ + method: 'POST', + url: 'https://api.getresponse.com/v3/tags', + headers: { + 'X-Auth-Token': `api-key ${context.auth.apiKey}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + data: { + name: name + } + }); + + const tag = response.data; + + return context.sendJson({ + tagId: tag.id, + name: tag.name, + href: tag.href, + createdAt: tag.createdAt + }, 'out'); + } +}; diff --git a/src/appmixer/getResponse/core/CreateTag/component.json b/src/appmixer/getResponse/core/CreateTag/component.json new file mode 100644 index 0000000000..d39871e307 --- /dev/null +++ b/src/appmixer/getResponse/core/CreateTag/component.json @@ -0,0 +1,65 @@ +{ + "name": "appmixer.getResponse.core.CreateTag", + "label": "Create Tag", + "author": "Appmixer ", + "description": "Create a new tag.", + "version": "1.0.0", + "auth": { + "service": "appmixer:getResponse" + }, + "quota": { + "manager": "appmixer:getResponse", + "resources": "requests" + }, + "inPorts": [ + { + "name": "in", + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + }, + "required": ["name"] + }, + "inspector": { + "inputs": { + "name": { + "type": "text", + "label": "Tag Name", + "index": 0, + "tooltip": "Tag name to create. Only alphanumeric characters and underscores are allowed." + } + } + } + } + ], + "outPorts": [ + { + "name": "out", + "schema": { + "type": "object", + "properties": { + "tagId": { + "type": "string", + "title": "Tag ID" + }, + "name": { + "type": "string", + "title": "Name" + }, + "href": { + "type": "string", + "title": "URL" + }, + "createdAt": { + "type": "string", + "title": "Created At" + } + } + } + } + ], + "icon": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI3LjguMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA4Ny45IDU2IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA4Ny45IDU2OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzAwQUJFOTt9Cgkuc3Qxe2ZpbGw6I0ZGRkZGRjt9Cjwvc3R5bGU+CjxnPgoJPGc+CgkJPHBhdGggY2xhc3M9InN0MCIgZD0iTTg3LjgsNTIuMWMwLDIuMS0xLjcsMy43LTMuNywzLjdIMy45Yy0yLjEsMC0zLjctMS43LTMuNy0zLjdWMy45YzAtMi4xLDEuNy0zLjcsMy43LTMuN0g4NAoJCQljMi4xLDAsMy43LDEuNywzLjcsMy43VjUyLjF6Ii8+Cgk8L2c+Cgk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODYuOSwxLjVDNzYuNiwxNy43LDYwLjUsMzIuNCw0My45LDMyLjFjLTYuMS0wLjEtMTMuMS0xLjktMTktNi43Yy00LjQtMy42LTguOC05LjItMTAuNS0xNy4yCgkJYy0wLjcsMC0xLjIsMC0xLjgsMGMtMi43LDAtNC4zLDIuNC00LDQuNmMwLjEsMC41LDAuMiwxLjEsMC4zLDEuN2MxLjMsNy44LDUuOSwxNi42LDEzLjYsMjIuM2M2LDQuNCwxMy40LDcuMywyMS41LDcuMwoJCWMxNC42LDAsMzAuMi05LjMsNDMuOC0zMy4yVjMuOUM4Ny44LDMsODcuNSwyLjIsODYuOSwxLjV6Ii8+CjwvZz4KPC9zdmc+Cg==" +} diff --git a/src/appmixer/getResponse/core/DeleteCampaign/DeleteCampaign.js b/src/appmixer/getResponse/core/DeleteCampaign/DeleteCampaign.js new file mode 100644 index 0000000000..ffdbcc0eed --- /dev/null +++ b/src/appmixer/getResponse/core/DeleteCampaign/DeleteCampaign.js @@ -0,0 +1,22 @@ +'use strict'; + +module.exports = { + async receive(context) { + const { campaignId } = context.messages.in.content; + + if (!campaignId) { + throw new context.CancelError('Campaign ID is required!'); + } + + await context.httpRequest({ + method: 'DELETE', + url: `https://api.getresponse.com/v3/campaigns/${campaignId}`, + headers: { + 'X-Auth-Token': `api-key ${context.auth.apiKey}`, + 'Content-Type': 'application/json' + } + }); + + return context.sendJson({}, 'out'); + } +}; diff --git a/src/appmixer/getResponse/core/DeleteCampaign/component.json b/src/appmixer/getResponse/core/DeleteCampaign/component.json new file mode 100644 index 0000000000..0b68225528 --- /dev/null +++ b/src/appmixer/getResponse/core/DeleteCampaign/component.json @@ -0,0 +1,47 @@ +{ + "name": "appmixer.getResponse.core.DeleteCampaign", + "label": "Delete Campaign", + "author": "Appmixer ", + "description": "Delete a campaign by ID.", + "version": "1.0.0", + "private": false, + "auth": { + "service": "appmixer:getResponse" + }, + "quota": { + "manager": "appmixer:getResponse", + "resources": "requests" + }, + "inPorts": [ + { + "name": "in", + "schema": { + "type": "object", + "properties": { + "campaignId": { + "type": "string" + } + }, + "required": [ + "campaignId" + ] + }, + "inspector": { + "inputs": { + "campaignId": { + "type": "text", + "label": "Campaign ID", + "tooltip": "The unique identifier of the campaign to delete.", + "index": 0 + } + } + } + } + ], + "outPorts": [ + { + "name": "out" + } + ], + "icon": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI3LjguMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA4Ny45IDU2IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA4Ny45IDU2OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzAwQUJFOTt9Cgkuc3Qxe2ZpbGw6I0ZGRkZGRjt9Cjwvc3R5bGU+CjxnPgoJPGc+CgkJPHBhdGggY2xhc3M9InN0MCIgZD0iTTg3LjgsNTIuMWMwLDIuMS0xLjcsMy43LTMuNywzLjdIMy45Yy0yLjEsMC0zLjctMS43LTMuNy0zLjdWMy45YzAtMi4xLDEuNy0zLjcsMy43LTMuN0g4NAoJCQljMi4xLDAsMy43LDEuNywzLjcsMy43VjUyLjF6Ii8+Cgk8L2c+Cgk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODYuOSwxLjVDNzYuNiwxNy43LDYwLjUsMzIuNCw0My45LDMyLjFjLTYuMS0wLjEtMTMuMS0xLjktMTktNi43Yy00LjQtMy42LTguOC05LjItMTAuNS0xNy4yCgkJYy0wLjcsMC0xLjIsMC0xLjgsMGMtMi43LDAtNC4zLDIuNC00LDQuNmMwLjEsMC41LDAuMiwxLjEsMC4zLDEuN2MxLjMsNy44LDUuOSwxNi42LDEzLjYsMjIuM2M2LDQuNCwxMy40LDcuMywyMS41LDcuMwoJCWMxNC42LDAsMzAuMi05LjMsNDMuOC0zMy4yVjMuOUM4Ny44LDMsODcuNSwyLjIsODYuOSwxLjV6Ii8+CjwvZz4KPC9zdmc+Cg==" +} \ No newline at end of file diff --git a/src/appmixer/getResponse/core/DeleteContact/DeleteContact.js b/src/appmixer/getResponse/core/DeleteContact/DeleteContact.js new file mode 100644 index 0000000000..e9dec9dfc6 --- /dev/null +++ b/src/appmixer/getResponse/core/DeleteContact/DeleteContact.js @@ -0,0 +1,24 @@ +'use strict'; + +module.exports = { + async receive(context) { + const { contactId } = context.messages.in.content; + + if (!contactId) { + throw new context.CancelError('Contact ID is required!'); + } + + // https://apireference.getresponse.com/#contacts + await context.httpRequest({ + method: 'DELETE', + url: `https://api.getresponse.com/v3/contacts/${contactId}`, + headers: { + 'X-Auth-Token': `api-key ${context.auth.apiKey}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + }); + + return context.sendJson({}, 'out'); + } +}; diff --git a/src/appmixer/getResponse/core/DeleteContact/component.json b/src/appmixer/getResponse/core/DeleteContact/component.json new file mode 100644 index 0000000000..84dd8da97b --- /dev/null +++ b/src/appmixer/getResponse/core/DeleteContact/component.json @@ -0,0 +1,46 @@ +{ + "name": "appmixer.getResponse.core.DeleteContact", + "label": "Delete Contact", + "author": "Appmixer ", + "description": "Delete a contact by ID. This action cannot be undone.", + "version": "1.0.0", + "auth": { + "service": "appmixer:getResponse" + }, + "quota": { + "manager": "appmixer:getResponse", + "resources": "requests" + }, + "inPorts": [ + { + "name": "in", + "schema": { + "type": "object", + "properties": { + "contactId": { + "type": "string" + } + }, + "required": [ + "contactId" + ] + }, + "inspector": { + "inputs": { + "contactId": { + "type": "text", + "label": "Contact ID", + "index": 0, + "tooltip": "The unique identifier of the contact to delete." + } + } + } + } + ], + "outPorts": [ + { + "name": "out" + } + ], + "icon": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI3LjguMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA4Ny45IDU2IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA4Ny45IDU2OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzAwQUJFOTt9Cgkuc3Qxe2ZpbGw6I0ZGRkZGRjt9Cjwvc3R5bGU+CjxnPgoJPGc+CgkJPHBhdGggY2xhc3M9InN0MCIgZD0iTTg3LjgsNTIuMWMwLDIuMS0xLjcsMy43LTMuNywzLjdIMy45Yy0yLjEsMC0zLjctMS43LTMuNy0zLjdWMy45YzAtMi4xLDEuNy0zLjcsMy43LTMuN0g4NAoJCQljMi4xLDAsMy43LDEuNywzLjcsMy43VjUyLjF6Ii8+Cgk8L2c+Cgk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODYuOSwxLjVDNzYuNiwxNy43LDYwLjUsMzIuNCw0My45LDMyLjFjLTYuMS0wLjEtMTMuMS0xLjktMTktNi43Yy00LjQtMy42LTguOC05LjItMTAuNS0xNy4yCgkJYy0wLjcsMC0xLjIsMC0xLjgsMGMtMi43LDAtNC4zLDIuNC00LDQuNmMwLjEsMC41LDAuMiwxLjEsMC4zLDEuN2MxLjMsNy44LDUuOSwxNi42LDEzLjYsMjIuM2M2LDQuNCwxMy40LDcuMywyMS41LDcuMwoJCWMxNC42LDAsMzAuMi05LjMsNDMuOC0zMy4yVjMuOUM4Ny44LDMsODcuNSwyLjIsODYuOSwxLjV6Ii8+CjwvZz4KPC9zdmc+Cg==" +} \ No newline at end of file diff --git a/src/appmixer/getResponse/core/DeleteTag/DeleteTag.js b/src/appmixer/getResponse/core/DeleteTag/DeleteTag.js new file mode 100644 index 0000000000..6ad5f31016 --- /dev/null +++ b/src/appmixer/getResponse/core/DeleteTag/DeleteTag.js @@ -0,0 +1,23 @@ +'use strict'; + +module.exports = { + async receive(context) { + const { tagId } = context.messages.in.content; + + if (!tagId) { + throw new context.CancelError('Tag ID is required!'); + } + + // https://apireference.getresponse.com/#tags + await context.httpRequest({ + method: 'DELETE', + url: `https://api.getresponse.com/v3/tags/${tagId}`, + headers: { + 'X-Auth-Token': `api-key ${context.auth.apiKey}`, + 'Content-Type': 'application/json' + } + }); + + return context.sendJson({}, 'out'); + } +}; diff --git a/src/appmixer/getResponse/core/DeleteTag/component.json b/src/appmixer/getResponse/core/DeleteTag/component.json new file mode 100644 index 0000000000..9e38e8f1a2 --- /dev/null +++ b/src/appmixer/getResponse/core/DeleteTag/component.json @@ -0,0 +1,47 @@ +{ + "name": "appmixer.getResponse.core.DeleteTag", + "label": "Delete Tag", + "author": "Appmixer ", + "description": "Delete a tag by ID.", + "version": "1.0.0", + "private": false, + "auth": { + "service": "appmixer:getResponse" + }, + "quota": { + "manager": "appmixer:getResponse", + "resources": "requests" + }, + "inPorts": [ + { + "name": "in", + "schema": { + "type": "object", + "properties": { + "tagId": { + "type": "string" + } + }, + "required": [ + "tagId" + ] + }, + "inspector": { + "inputs": { + "tagId": { + "type": "text", + "label": "Tag ID", + "tooltip": "The unique identifier of the tag to delete.", + "index": 0 + } + } + } + } + ], + "outPorts": [ + { + "name": "out" + } + ], + "icon": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI3LjguMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA4Ny45IDU2IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA4Ny45IDU2OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzAwQUJFOTt9Cgkuc3Qxe2ZpbGw6I0ZGRkZGRjt9Cjwvc3R5bGU+CjxnPgoJPGc+CgkJPHBhdGggY2xhc3M9InN0MCIgZD0iTTg3LjgsNTIuMWMwLDIuMS0xLjcsMy43LTMuNywzLjdIMy45Yy0yLjEsMC0zLjctMS43LTMuNy0zLjdWMy45YzAtMi4xLDEuNy0zLjcsMy43LTMuN0g4NAoJCQljMi4xLDAsMy43LDEuNywzLjcsMy43VjUyLjF6Ii8+Cgk8L2c+Cgk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODYuOSwxLjVDNzYuNiwxNy43LDYwLjUsMzIuNCw0My45LDMyLjFjLTYuMS0wLjEtMTMuMS0xLjktMTktNi43Yy00LjQtMy42LTguOC05LjItMTAuNS0xNy4yCgkJYy0wLjcsMC0xLjIsMC0xLjgsMGMtMi43LDAtNC4zLDIuNC00LDQuNmMwLjEsMC41LDAuMiwxLjEsMC4zLDEuN2MxLjMsNy44LDUuOSwxNi42LDEzLjYsMjIuM2M2LDQuNCwxMy40LDcuMywyMS41LDcuMwoJCWMxNC42LDAsMzAuMi05LjMsNDMuOC0zMy4yVjMuOUM4Ny44LDMsODcuNSwyLjIsODYuOSwxLjV6Ii8+CjwvZz4KPC9zdmc+Cg==" +} \ No newline at end of file diff --git a/src/appmixer/getResponse/core/FindCampaigns/FindCampaigns.js b/src/appmixer/getResponse/core/FindCampaigns/FindCampaigns.js new file mode 100644 index 0000000000..a8af97e35e --- /dev/null +++ b/src/appmixer/getResponse/core/FindCampaigns/FindCampaigns.js @@ -0,0 +1,74 @@ +'use strict'; + +const lib = require('../../lib'); + +const schema = { + 'id': { 'type': 'string', 'title': 'Campaign ID' }, + 'name': { 'type': 'string', 'title': 'Name' }, + 'status': { 'type': 'string', 'title': 'Status' }, + 'createdOn': { 'type': 'string', 'format': 'date-time', 'title': 'Created On' }, + 'updatedOn': { 'type': 'string', 'format': 'date-time', 'title': 'Updated On' }, + 'description': { 'type': 'string', 'title': 'Description' }, + 'defaultFrom': { + 'type': 'object', + 'properties': { + 'fromFieldId': { 'type': 'string', 'title': 'Default From.From Field Id' }, + 'email': { 'type': 'string', 'title': 'Default From.Email' }, + 'name': { 'type': 'string', 'title': 'Default From.Name' } + }, + 'title': 'Default From' + }, + 'defaultReplyTo': { + 'type': 'object', + 'properties': { + 'fromFieldId': { 'type': 'string', 'title': 'Default Reply To.From Field Id' }, + 'email': { 'type': 'string', 'title': 'Default Reply To.Email' }, + 'name': { 'type': 'string', 'title': 'Default Reply To.Name' } + }, + 'title': 'Default Reply To' + }, + 'languageCode': { 'type': 'string', 'title': 'Language Code' }, + 'timezone': { 'type': 'string', 'title': 'Timezone' }, + 'href': { 'type': 'string', 'title': 'Href' } +}; + +module.exports = { + async receive(context) { + + const { status, sort, outputType } = context.messages.in.content; + + if (context.properties.generateOutputPortOptions) { + return lib.getOutputPortOptions(context, outputType, schema, { label: 'Campaigns' }); + } + + const params = {}; + + if (status) { + params.status = status; + } + + if (sort) { + params.sort = sort; + } + + // https://apidocs.getresponse.com/#operation/getCampaigns + const { data } = await context.httpRequest({ + method: 'GET', + url: 'https://api.getresponse.com/v3/campaigns', + headers: { + 'X-Auth-Token': `api-key ${context.auth.apiKey}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + params + }); + + const campaigns = Array.isArray(data) ? data : []; + + if (campaigns.length === 0) { + return context.sendJson({}, 'notFound'); + } + + return lib.sendArrayOutput({ context, records: campaigns, outputType }); + } +}; diff --git a/src/appmixer/getResponse/core/FindCampaigns/component.json b/src/appmixer/getResponse/core/FindCampaigns/component.json new file mode 100644 index 0000000000..c1fd768f92 --- /dev/null +++ b/src/appmixer/getResponse/core/FindCampaigns/component.json @@ -0,0 +1,119 @@ +{ + "name": "appmixer.getResponse.core.FindCampaigns", + "label": "Find Campaigns", + "author": "Appmixer ", + "description": "Search for campaigns in your GetResponse account. Returns a maximum of 1000 campaigns per request.", + "version": "1.0.0", + "private": false, + "auth": { + "service": "appmixer:getResponse" + }, + "quota": { + "manager": "appmixer:getResponse", + "resources": "requests" + }, + "inPorts": [ + { + "name": "in", + "schema": { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "sort": { + "type": "string" + }, + "outputType": { + "type": "string" + } + } + }, + "inspector": { + "inputs": { + "status": { + "type": "select", + "tooltip": "Filter campaigns by status.", + "label": "Status", + "options": [ + { + "label": "Active", + "value": "active" + }, + { + "label": "Inactive", + "value": "inactive" + } + ], + "index": 0 + }, + "sort": { + "type": "select", + "tooltip": "Sort order for campaigns.", + "label": "Sort", + "options": [ + { + "label": "Name (A-Z)", + "value": "name" + }, + { + "label": "Created On (Newest)", + "value": "-createdOn" + }, + { + "label": "Created On (Oldest)", + "value": "createdOn" + } + ], + "index": 1 + }, + "outputType": { + "type": "select", + "label": "Output Type", + "index": 2, + "defaultValue": "array", + "tooltip": "Choose whether you want to receive the result set as one complete list, or first item only or one item at a time or stream the items to a file.", + "options": [ + { + "label": "First Item Only", + "value": "first" + }, + { + "label": "All items at once", + "value": "array" + }, + { + "label": "One item at a time", + "value": "object" + }, + { + "label": "Store to CSV file", + "value": "file" + } + ] + } + } + } + } + ], + "outPorts": [ + { + "name": "out", + "source": { + "url": "/component/appmixer/getResponse/core/FindCampaigns?outPort=out", + "data": { + "properties": { + "generateOutputPortOptions": true + }, + "messages": { + "in/outputType": "inputs/in/outputType" + } + } + } + }, + { + "name": "notFound" + } + ], + "icon": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI3LjguMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA4Ny45IDU2IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA4Ny45IDU2OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzAwQUJFOTt9Cgkuc3Qxe2ZpbGw6I0ZGRkZGRjt9Cjwvc3R5bGU+CjxnPgoJPGc+CgkJPHBhdGggY2xhc3M9InN0MCIgZD0iTTg3LjgsNTIuMWMwLDIuMS0xLjcsMy43LTMuNywzLjdIMy45Yy0yLjEsMC0zLjctMS43LTMuNy0zLjdWMy45YzAtMi4xLDEuNy0zLjcsMy43LTMuN0g4NAoJCQljMi4xLDAsMy43LDEuNywzLjcsMy43VjUyLjF6Ii8+Cgk8L2c+Cgk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODYuOSwxLjVDNzYuNiwxNy43LDYwLjUsMzIuNCw0My45LDMyLjFjLTYuMS0wLjEtMTMuMS0xLjktMTktNi43Yy00LjQtMy42LTguOC05LjItMTAuNS0xNy4yCgkJYy0wLjcsMC0xLjIsMC0xLjgsMGMtMi43LDAtNC4zLDIuNC00LDQuNmMwLjEsMC41LDAuMiwxLjEsMC4zLDEuN2MxLjMsNy44LDUuOSwxNi42LDEzLjYsMjIuM2M2LDQuNCwxMy40LDcuMywyMS41LDcuMwoJCWMxNC42LDAsMzAuMi05LjMsNDMuOC0zMy4yVjMuOUM4Ny44LDMsODcuNSwyLjIsODYuOSwxLjV6Ii8+CjwvZz4KPC9zdmc+Cg==" +} \ No newline at end of file diff --git a/src/appmixer/getResponse/core/FindContacts/FindContacts.js b/src/appmixer/getResponse/core/FindContacts/FindContacts.js new file mode 100644 index 0000000000..ad738f32cf --- /dev/null +++ b/src/appmixer/getResponse/core/FindContacts/FindContacts.js @@ -0,0 +1,101 @@ +'use strict'; + +const lib = require('../../lib'); + +const schema = { + 'contactId': { 'type': 'string', 'title': 'Contact Id' }, + 'href': { 'type': 'string', 'title': 'Href' }, + 'name': { 'type': 'string', 'title': 'Name' }, + 'email': { 'type': 'string', 'title': 'Email' }, + 'note': { 'type': ['string', 'null'], 'title': 'Note' }, + 'state': { 'type': 'string', 'title': 'State' }, + 'dayOfCycle': { 'type': ['string', 'number'], 'title': 'Day Of Cycle' }, + 'changedOn': { 'type': 'string', 'title': 'Changed On' }, + 'timeZone': { 'type': 'string', 'title': 'Time Zone' }, + 'campaign': { 'type': 'object', 'properties': { 'campaignId': { 'type': 'string', 'title': 'Campaign.Campaign Id' }, 'href': { 'type': 'string', 'title': 'Campaign.Href' }, 'name': { 'type': 'string', 'title': 'Campaign.Name' } }, 'title': 'Campaign' }, + 'ipAddress': { 'type': 'string', 'title': 'Ip Address' }, + 'activities': { 'type': 'string', 'title': 'Activities' }, + 'createdOn': { 'type': 'string', 'title': 'Created On' }, + 'origin': { 'type': 'string', 'title': 'Origin' }, + 'scoring': { 'type': ['number', 'null'], 'title': 'Scoring' }, + 'engagementScore': { 'type': 'number', 'title': 'Engagement Score' }, + 'customFieldValues': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'customFieldId': { 'type': 'string', 'title': 'Custom Field Values.Custom Field Id' }, 'value': { 'type': 'array', 'items': { 'type': 'string' }, 'title': 'Custom Field Values.Value' } } }, 'title': 'Custom Field Values' }, + 'tags': { 'type': 'array', 'items': { 'type': 'object', 'properties': { 'tagId': { 'type': 'string', 'title': 'Tags.Tag Id' }, 'name': { 'type': 'string', 'title': 'Tags.Name' } } }, 'title': 'Tags' } +}; + +module.exports = { + async receive(context) { + + const { + email, + name, + campaignId, + state, + createdOnFrom, + createdOnTo, + sortBy, + sortOrder, + outputType + } = context.messages.in.content; + + if (context.properties.generateOutputPortOptions) { + return lib.getOutputPortOptions(context, outputType, schema, { label: 'Contacts', value: 'contacts' }); + } + + // Build query parameters + const params = {}; + + if (email) { + params['query[email]'] = email; + } + if (name) { + params['query[name]'] = name; + } + if (campaignId) { + params['query[campaignId]'] = campaignId; + } + if (state) { + params['query[state]'] = state; + } + if (createdOnFrom) { + params['query[createdOn][from]'] = createdOnFrom; + } + if (createdOnTo) { + params['query[createdOn][to]'] = createdOnTo; + } + if (sortBy) { + params['sort[' + sortBy + ']'] = sortOrder || 'asc'; + } + + // Set limit to maximum of 1000 per API documentation + params.limit = 1000; + + // Retrieve contacts from getResponse API + // https://apireference.getresponse.com/#contacts + const response = await context.httpRequest({ + method: 'GET', + url: 'https://api.getresponse.com/v3/contacts', + headers: { + 'X-Auth-Token': `api-key ${context.auth.apiKey}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + params + }); + + // GetResponse API v3 returns contacts in _embedded.contacts array + let contacts = []; + if (response.data && response.data._embedded && Array.isArray(response.data._embedded.contacts)) { + contacts = response.data._embedded.contacts; + } else if (Array.isArray(response.data)) { + // Fallback for direct array response + contacts = response.data; + } + + if (contacts.length === 0) { + return context.sendJson({}, 'notFound'); + } + + return lib.sendArrayOutput({ context, records: contacts, outputType }); + } +}; diff --git a/src/appmixer/getResponse/core/FindContacts/component.json b/src/appmixer/getResponse/core/FindContacts/component.json new file mode 100644 index 0000000000..b62f6ec770 --- /dev/null +++ b/src/appmixer/getResponse/core/FindContacts/component.json @@ -0,0 +1,204 @@ +{ + "name": "appmixer.getResponse.core.FindContacts", + "label": "Find Contacts", + "author": "Appmixer ", + "description": "Retrieve contacts with optional filters. Returns a maximum of 1000 contacts per request.", + "version": "1.0.0", + "private": false, + "auth": { + "service": "appmixer:getResponse" + }, + "quota": { + "manager": "appmixer:getResponse", + "resources": "requests", + "scope": { + "userId": "{{userId}}" + } + }, + "inPorts": [ + { + "name": "in", + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "name": { + "type": "string" + }, + "campaignId": { + "type": "string" + }, + "state": { + "type": "string" + }, + "createdOnFrom": { + "type": "string", + "format": "date-time" + }, + "createdOnTo": { + "type": "string", + "format": "date-time" + }, + "sortBy": { + "type": "string" + }, + "sortOrder": { + "type": "string" + }, + "outputType": { + "type": "string" + } + } + }, + "inspector": { + "inputs": { + "email": { + "type": "text", + "tooltip": "Filter by exact email address.", + "label": "Email", + "index": 0 + }, + "name": { + "type": "text", + "tooltip": "Filter by contact name.", + "label": "Name", + "index": 1 + }, + "campaignId": { + "type": "text", + "tooltip": "Filter by campaign/list ID to return contacts from a specific list.", + "label": "Campaign ID", + "index": 2 + }, + "state": { + "type": "select", + "tooltip": "Filter by contact state.", + "label": "State", + "index": 3, + "options": [ + { + "label": "All", + "value": "" + }, + { + "label": "Active", + "value": "active" + }, + { + "label": "Unconfirmed", + "value": "unconfirmed" + }, + { + "label": "Removed", + "value": "removed" + }, + { + "label": "Bounced", + "value": "bounced" + } + ] + }, + "createdOnFrom": { + "type": "date-time", + "tooltip": "Return contacts created on or after this date and time (ISO 8601).", + "label": "Created On From", + "index": 4 + }, + "createdOnTo": { + "type": "date-time", + "tooltip": "Return contacts created on or before this date and time (ISO 8601).", + "label": "Created On To", + "index": 5 + }, + "sortBy": { + "type": "select", + "tooltip": "Field to sort by.", + "label": "Sort By", + "index": 6, + "options": [ + { + "label": "None", + "value": "" + }, + { + "label": "Created On", + "value": "createdOn" + }, + { + "label": "Name", + "value": "name" + }, + { + "label": "Email", + "value": "email" + } + ] + }, + "sortOrder": { + "type": "select", + "tooltip": "Sort order.", + "label": "Sort Order", + "index": 7, + "options": [ + { + "label": "Ascending", + "value": "asc" + }, + { + "label": "Descending", + "value": "desc" + } + ] + }, + "outputType": { + "type": "select", + "label": "Output Type", + "index": 8, + "defaultValue": "array", + "tooltip": "Choose whether you want to receive the result set as one complete list, or first item only or one item at a time or stream the items to a file.", + "options": [ + { + "label": "First Item Only", + "value": "first" + }, + { + "label": "All items at once", + "value": "array" + }, + { + "label": "One item at a time", + "value": "object" + }, + { + "label": "Store to CSV file", + "value": "file" + } + ] + } + } + } + } + ], + "outPorts": [ + { + "name": "out", + "source": { + "url": "/component/appmixer/getResponse/core/FindContacts?outPort=out", + "data": { + "properties": { + "generateOutputPortOptions": true + }, + "messages": { + "in/outputType": "inputs/in/outputType" + } + } + } + }, + { + "name": "notFound" + } + ], + "icon": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI3LjguMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA4Ny45IDU2IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA4Ny45IDU2OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzAwQUJFOTt9Cgkuc3Qxe2ZpbGw6I0ZGRkZGRjt9Cjwvc3R5bGU+CjxnPgoJPGc+CgkJPHBhdGggY2xhc3M9InN0MCIgZD0iTTg3LjgsNTIuMWMwLDIuMS0xLjcsMy43LTMuNywzLjdIMy45Yy0yLjEsMC0zLjctMS43LTMuNy0zLjdWMy45YzAtMi4xLDEuNy0zLjcsMy43LTMuN0g4NAoJCQljMi4xLDAsMy43LDEuNywzLjcsMy43VjUyLjF6Ii8+Cgk8L2c+Cgk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODYuOSwxLjVDNzYuNiwxNy43LDYwLjUsMzIuNCw0My45LDMyLjFjLTYuMS0wLjEtMTMuMS0xLjktMTktNi43Yy00LjQtMy42LTguOC05LjItMTAuNS0xNy4yCgkJYy0wLjcsMC0xLjIsMC0xLjgsMGMtMi43LDAtNC4zLDIuNC00LDQuNmMwLjEsMC41LDAuMiwxLjEsMC4zLDEuN2MxLjMsNy44LDUuOSwxNi42LDEzLjYsMjIuM2M2LDQuNCwxMy40LDcuMywyMS41LDcuMwoJCWMxNC42LDAsMzAuMi05LjMsNDMuOC0zMy4yVjMuOUM4Ny44LDMsODcuNSwyLjIsODYuOSwxLjV6Ii8+CjwvZz4KPC9zdmc+Cg==" +} \ No newline at end of file diff --git a/src/appmixer/getResponse/core/FindTags/FindTags.js b/src/appmixer/getResponse/core/FindTags/FindTags.js new file mode 100644 index 0000000000..19855c9a3c --- /dev/null +++ b/src/appmixer/getResponse/core/FindTags/FindTags.js @@ -0,0 +1,47 @@ +'use strict'; + +const lib = require('../../lib'); + +const schema = { + 'gid': { 'type': 'string', 'title': 'Tag ID' }, + 'name': { 'type': 'string', 'title': 'Name' }, + 'createdOn': { 'type': 'string', 'title': 'Created On' } +}; + +module.exports = { + + async receive(context) { + + const { queryName, outputType } = context.messages.in.content; + + if (context.properties.generateOutputPortOptions) { + return lib.getOutputPortOptions(context, outputType, schema, { label: 'Tags', value: 'tags' }); + } + + // https://apireference.getresponse.com/#tags + const params = {}; + + if (queryName) { + params.query = `name:${queryName}`; + } + + const { data } = await context.httpRequest({ + method: 'GET', + url: 'https://api.getresponse.com/v3/tags', + headers: { + 'X-Auth-Token': `api-key ${context.auth.apiKey}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + params + }); + + const tags = data || []; + + if (tags.length === 0) { + return context.sendJson({}, 'notFound'); + } + + return lib.sendArrayOutput({ context, records: tags, outputType }); + } +}; diff --git a/src/appmixer/getResponse/core/FindTags/component.json b/src/appmixer/getResponse/core/FindTags/component.json new file mode 100644 index 0000000000..d1d21d6cd2 --- /dev/null +++ b/src/appmixer/getResponse/core/FindTags/component.json @@ -0,0 +1,85 @@ +{ + "name": "appmixer.getResponse.core.FindTags", + "label": "Find Tags", + "description": "Retrieve all tags. Returns a maximum of 1000 tags.", + "author": "Appmixer ", + "version": "1.0.0", + "auth": { + "service": "appmixer:getResponse" + }, + "quota": { + "manager": "appmixer:getResponse", + "resources": "requests" + }, + "inPorts": [ + { + "name": "in", + "schema": { + "type": "object", + "properties": { + "queryName": { + "type": "string" + }, + "outputType": { + "type": "string" + } + } + }, + "inspector": { + "inputs": { + "queryName": { + "type": "text", + "label": "Query Name", + "index": 0, + "tooltip": "Filter tags by name (substring match)." + }, + "outputType": { + "type": "select", + "label": "Output Type", + "index": 1, + "defaultValue": "array", + "tooltip": "Choose whether you want to receive the result set as one complete list, or first item only or one item at a time or stream the items to a file.", + "options": [ + { + "label": "First Item Only", + "value": "first" + }, + { + "label": "All items at once", + "value": "array" + }, + { + "label": "One item at a time", + "value": "object" + }, + { + "label": "Store to CSV file", + "value": "file" + } + ] + } + } + } + } + ], + "outPorts": [ + { + "name": "out", + "source": { + "url": "/component/appmixer/getResponse/core/FindTags?outPort=out", + "data": { + "properties": { + "generateOutputPortOptions": true + }, + "messages": { + "in/outputType": "inputs/in/outputType" + } + } + } + }, + { + "name": "notFound" + } + ], + "icon": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI3LjguMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA4Ny45IDU2IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA4Ny45IDU2OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzAwQUJFOTt9Cgkuc3Qxe2ZpbGw6I0ZGRkZGRjt9Cjwvc3R5bGU+CjxnPgoJPGc+CgkJPHBhdGggY2xhc3M9InN0MCIgZD0iTTg3LjgsNTIuMWMwLDIuMS0xLjcsMy43LTMuNywzLjdIMy45Yy0yLjEsMC0zLjctMS43LTMuNy0zLjdWMy45YzAtMi4xLDEuNy0zLjcsMy43LTMuN0g4NAoJCQljMi4xLDAsMy43LDEuNywzLjcsMy43VjUyLjF6Ii8+Cgk8L2c+Cgk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODYuOSwxLjVDNzYuNiwxNy43LDYwLjUsMzIuNCw0My45LDMyLjFjLTYuMS0wLjEtMTMuMS0xLjktMTktNi43Yy00LjQtMy42LTguOC05LjItMTAuNS0xNy4yCgkJYy0wLjcsMC0xLjIsMC0xLjgsMGMtMi43LDAtNC4zLDIuNC00LDQuNmMwLjEsMC41LDAuMiwxLjEsMC4zLDEuN2MxLjMsNy44LDUuOSwxNi42LDEzLjYsMjIuM2M2LDQuNCwxMy40LDcuMywyMS41LDcuMwoJCWMxNC42LDAsMzAuMi05LjMsNDMuOC0zMy4yVjMuOUM4Ny44LDMsODcuNSwyLjIsODYuOSwxLjV6Ii8+CjwvZz4KPC9zdmc+Cg==" +} \ No newline at end of file diff --git a/src/appmixer/getResponse/core/GetContact/GetContact.js b/src/appmixer/getResponse/core/GetContact/GetContact.js new file mode 100644 index 0000000000..bace1181e1 --- /dev/null +++ b/src/appmixer/getResponse/core/GetContact/GetContact.js @@ -0,0 +1,24 @@ +'use strict'; + +module.exports = { + async receive(context) { + const { contactId } = context.messages.in.content; + + if (!contactId) { + throw new context.CancelError('Contact ID is required!'); + } + + // https://apireference.getresponse.com/#contacts + const { data } = await context.httpRequest({ + method: 'GET', + url: `https://api.getresponse.com/v3/contacts/${contactId}`, + headers: { + 'X-Auth-Token': `api-key ${context.auth.apiKey}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json' + } + }); + + return context.sendJson(data, 'out'); + } +}; diff --git a/src/appmixer/getResponse/core/GetContact/component.json b/src/appmixer/getResponse/core/GetContact/component.json new file mode 100644 index 0000000000..a8c39b313e --- /dev/null +++ b/src/appmixer/getResponse/core/GetContact/component.json @@ -0,0 +1,123 @@ +{ + "name": "appmixer.getResponse.core.GetContact", + "label": "Get Contact", + "description": "Retrieve a single contact by its unique identifier.", + "author": "Appmixer ", + "version": "1.0.0", + "private": false, + "auth": { + "service": "appmixer:getResponse" + }, + "quota": { + "manager": "appmixer:getResponse", + "resources": "requests" + }, + "inPorts": [ + { + "name": "in", + "schema": { + "type": "object", + "properties": { + "contactId": { + "type": "string" + } + }, + "required": [ + "contactId" + ] + }, + "inspector": { + "inputs": { + "contactId": { + "type": "text", + "index": 0, + "label": "Contact ID", + "tooltip": "The unique identifier of the contact." + } + } + } + } + ], + "outPorts": [ + { + "name": "out", + "schema": { + "type": "object", + "properties": { + "contactId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "state": { + "type": "string" + }, + "dayOfCycle": { + "type": "number" + }, + "campaign": { + "type": "object", + "properties": { + "campaignId": { + "type": "string", + "title": "Campaign.Campaign ID" + } + } + }, + "ipAddress": { + "type": "string" + }, + "createdOn": { + "type": "string" + }, + "origin": { + "type": "string" + }, + "scoring": { + "type": "number" + }, + "customFieldValues": { + "type": "array", + "items": { + "type": "object", + "properties": { + "customFieldId": { + "type": "string", + "title": "Custom Field Values.Custom Field ID" + }, + "value": { + "type": "array", + "items": { + "type": "string" + }, + "title": "Custom Field Values.Value" + } + } + } + }, + "tags": { + "type": "array", + "items": { + "type": "object", + "properties": { + "tagId": { + "type": "string", + "title": "Tags.Tag ID" + }, + "name": { + "type": "string", + "title": "Tags.Name" + } + } + } + } + } + } + } + ], + "icon": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI3LjguMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA4Ny45IDU2IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA4Ny45IDU2OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzAwQUJFOTt9Cgkuc3Qxe2ZpbGw6I0ZGRkZGRjt9Cjwvc3R5bGU+CjxnPgoJPGc+CgkJPHBhdGggY2xhc3M9InN0MCIgZD0iTTg3LjgsNTIuMWMwLDIuMS0xLjcsMy43LTMuNywzLjdIMy45Yy0yLjEsMC0zLjctMS43LTMuNy0zLjdWMy45YzAtMi4xLDEuNy0zLjcsMy43LTMuN0g4NAoJCQljMi4xLDAsMy43LDEuNywzLjcsMy43VjUyLjF6Ii8+Cgk8L2c+Cgk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODYuOSwxLjVDNzYuNiwxNy43LDYwLjUsMzIuNCw0My45LDMyLjFjLTYuMS0wLjEtMTMuMS0xLjktMTktNi43Yy00LjQtMy42LTguOC05LjItMTAuNS0xNy4yCgkJYy0wLjcsMC0xLjIsMC0xLjgsMGMtMi43LDAtNC4zLDIuNC00LDQuNmMwLjEsMC41LDAuMiwxLjEsMC4zLDEuN2MxLjMsNy44LDUuOSwxNi42LDEzLjYsMjIuM2M2LDQuNCwxMy40LDcuMywyMS41LDcuMwoJCWMxNC42LDAsMzAuMi05LjMsNDMuOC0zMy4yVjMuOUM4Ny44LDMsODcuNSwyLjIsODYuOSwxLjV6Ii8+CjwvZz4KPC9zdmc+Cg==" +} \ No newline at end of file diff --git a/src/appmixer/getResponse/core/UpdateCampaign/UpdateCampaign.js b/src/appmixer/getResponse/core/UpdateCampaign/UpdateCampaign.js new file mode 100644 index 0000000000..54609859d2 --- /dev/null +++ b/src/appmixer/getResponse/core/UpdateCampaign/UpdateCampaign.js @@ -0,0 +1,76 @@ +'use strict'; + +module.exports = { + async receive(context) { + const { + campaignId, + name, + languageCode, + isDefault, + confirmationSubject, + confirmationFromName, + confirmationFromEmail, + confirmationReplyToEmail, + confirmationRedirectUrl + } = context.messages.in.content; + + if (!campaignId) { + throw new context.CancelError('Campaign ID is required!'); + } + + const body = {}; + + if (name !== undefined) { + body.name = name; + } + + if (languageCode !== undefined) { + body.languageCode = languageCode; + } + + if (isDefault !== undefined) { + body.isDefault = isDefault; + } + + // Build confirmation object if any confirmation fields are provided + if (confirmationSubject !== undefined || confirmationFromName !== undefined || + confirmationFromEmail !== undefined || confirmationReplyToEmail !== undefined || + confirmationRedirectUrl !== undefined) { + body.confirmation = {}; + + if (confirmationSubject !== undefined) { + body.confirmation.subscriptionConfirmationSubject = confirmationSubject; + } + if (confirmationFromName !== undefined || confirmationFromEmail !== undefined) { + body.confirmation.fromField = {}; + if (confirmationFromName !== undefined) { + body.confirmation.fromField.fromFieldName = confirmationFromName; + } + if (confirmationFromEmail !== undefined) { + body.confirmation.fromField.fromFieldId = confirmationFromEmail; + } + } + if (confirmationReplyToEmail !== undefined) { + body.confirmation.replyTo = { + fromFieldId: confirmationReplyToEmail + }; + } + if (confirmationRedirectUrl !== undefined) { + body.confirmation.redirectUrl = confirmationRedirectUrl; + } + } + + const response = await context.httpRequest({ + method: 'POST', + url: `https://api.getresponse.com/v3/campaigns/${campaignId}`, + headers: { + 'X-Auth-Token': `api-key ${context.auth.apiKey}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + data: body + }); + + return context.sendJson(response.data, 'out'); + } +}; diff --git a/src/appmixer/getResponse/core/UpdateCampaign/component.json b/src/appmixer/getResponse/core/UpdateCampaign/component.json new file mode 100644 index 0000000000..0640abf659 --- /dev/null +++ b/src/appmixer/getResponse/core/UpdateCampaign/component.json @@ -0,0 +1,119 @@ +{ + "name": "appmixer.getResponse.core.UpdateCampaign", + "label": "Update Campaign", + "author": "Appmixer ", + "description": "Update an existing campaign by ID.", + "version": "1.0.0", + "private": false, + "auth": { + "service": "appmixer:getResponse" + }, + "quota": { + "manager": "appmixer:getResponse", + "resources": "requests" + }, + "inPorts": [ + { + "name": "in", + "schema": { + "type": "object", + "properties": { + "campaignId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "languageCode": { + "type": "string" + }, + "isDefault": { + "type": "boolean" + }, + "confirmationSubject": { + "type": "string" + }, + "confirmationFromName": { + "type": "string" + }, + "confirmationFromEmail": { + "type": "string" + }, + "confirmationReplyToEmail": { + "type": "string" + }, + "confirmationRedirectUrl": { + "type": "string" + } + }, + "required": [ + "campaignId" + ] + }, + "inspector": { + "inputs": { + "campaignId": { + "type": "text", + "tooltip": "The unique identifier of the campaign to update.", + "label": "Campaign ID", + "index": 0 + }, + "name": { + "type": "text", + "tooltip": "New name for the campaign.", + "label": "Campaign Name", + "index": 1 + }, + "languageCode": { + "type": "text", + "tooltip": "Language code (e.g., EN, DE, PL).", + "label": "Language Code", + "index": 2 + }, + "isDefault": { + "type": "toggle", + "tooltip": "Set as default campaign.", + "label": "Is Default", + "index": 3 + }, + "confirmationSubject": { + "type": "text", + "tooltip": "Subject for subscription confirmation email.", + "label": "Confirmation Subject", + "index": 4 + }, + "confirmationFromName": { + "type": "text", + "tooltip": "From name for confirmation email.", + "label": "Confirmation From Name", + "index": 5 + }, + "confirmationFromEmail": { + "type": "text", + "tooltip": "Sender email address for the confirmation email.", + "label": "Confirmation From Email", + "index": 6 + }, + "confirmationReplyToEmail": { + "type": "text", + "tooltip": "Reply-to email address for the confirmation email.", + "label": "Confirmation Reply-To Email", + "index": 7 + }, + "confirmationRedirectUrl": { + "type": "text", + "tooltip": "URL to redirect after subscription confirmation.", + "label": "Confirmation Redirect URL", + "index": 8 + } + } + } + } + ], + "outPorts": [ + { + "name": "out" + } + ], + "icon": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI3LjguMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA4Ny45IDU2IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA4Ny45IDU2OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzAwQUJFOTt9Cgkuc3Qxe2ZpbGw6I0ZGRkZGRjt9Cjwvc3R5bGU+CjxnPgoJPGc+CgkJPHBhdGggY2xhc3M9InN0MCIgZD0iTTg3LjgsNTIuMWMwLDIuMS0xLjcsMy43LTMuNywzLjdIMy45Yy0yLjEsMC0zLjctMS43LTMuNy0zLjdWMy45YzAtMi4xLDEuNy0zLjcsMy43LTMuN0g4NAoJCQljMi4xLDAsMy43LDEuNywzLjcsMy43VjUyLjF6Ii8+Cgk8L2c+Cgk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODYuOSwxLjVDNzYuNiwxNy43LDYwLjUsMzIuNCw0My45LDMyLjFjLTYuMS0wLjEtMTMuMS0xLjktMTktNi43Yy00LjQtMy42LTguOC05LjItMTAuNS0xNy4yCgkJYy0wLjcsMC0xLjIsMC0xLjgsMGMtMi43LDAtNC4zLDIuNC00LDQuNmMwLjEsMC41LDAuMiwxLjEsMC4zLDEuN2MxLjMsNy44LDUuOSwxNi42LDEzLjYsMjIuM2M2LDQuNCwxMy40LDcuMywyMS41LDcuMwoJCWMxNC42LDAsMzAuMi05LjMsNDMuOC0zMy4yVjMuOUM4Ny44LDMsODcuNSwyLjIsODYuOSwxLjV6Ii8+CjwvZz4KPC9zdmc+Cg==" +} \ No newline at end of file diff --git a/src/appmixer/getResponse/core/UpdateContact/UpdateContact.js b/src/appmixer/getResponse/core/UpdateContact/UpdateContact.js new file mode 100644 index 0000000000..e52e002750 --- /dev/null +++ b/src/appmixer/getResponse/core/UpdateContact/UpdateContact.js @@ -0,0 +1,77 @@ +'use strict'; + +module.exports = { + async receive(context) { + const { + contactId, + name, + dayOfCycle, + campaignId, + note, + scoring, + customFieldId, + customFieldValue, + tagId + } = context.messages.in.content; + + if (!contactId) { + throw new context.CancelError('Contact ID is required!'); + } + + // Build request body with only provided fields + const body = {}; + + if (name !== undefined) { + body.name = name; + } + + if (dayOfCycle !== undefined) { + body.dayOfCycle = dayOfCycle; + } + + if (campaignId !== undefined) { + body.campaign = { + campaignId: campaignId + }; + } + + if (note !== undefined) { + body.note = note; + } + + if (scoring !== undefined) { + body.scoring = scoring; + } + + if (customFieldId !== undefined && customFieldValue !== undefined) { + body.customFieldValues = [ + { + customFieldId: customFieldId, + value: [customFieldValue] + } + ]; + } + + if (tagId !== undefined) { + body.tags = [ + { + tagId: tagId + } + ]; + } + + // https://apireference.getresponse.com/#operation/updateContact + await context.httpRequest({ + method: 'POST', + url: `https://api.getresponse.com/v3/contacts/${contactId}`, + headers: { + 'X-Auth-Token': `api-key ${context.auth.apiKey}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + data: body + }); + + return context.sendJson({}, 'out'); + } +}; diff --git a/src/appmixer/getResponse/core/UpdateContact/component.json b/src/appmixer/getResponse/core/UpdateContact/component.json new file mode 100644 index 0000000000..f5f18c6b35 --- /dev/null +++ b/src/appmixer/getResponse/core/UpdateContact/component.json @@ -0,0 +1,119 @@ +{ + "name": "appmixer.getResponse.core.UpdateContact", + "label": "Update Contact", + "author": "Appmixer ", + "description": "Update fields of an existing contact by ID.", + "version": "1.0.0", + "private": false, + "auth": { + "service": "appmixer:getResponse" + }, + "quota": { + "manager": "appmixer:getResponse", + "resources": "requests" + }, + "inPorts": [ + { + "name": "in", + "schema": { + "type": "object", + "properties": { + "contactId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "dayOfCycle": { + "type": "integer" + }, + "campaignId": { + "type": "string" + }, + "note": { + "type": "string" + }, + "scoring": { + "type": "integer" + }, + "customFieldId": { + "type": "string" + }, + "customFieldValue": { + "type": "string" + }, + "tagId": { + "type": "string" + } + }, + "required": [ + "contactId" + ] + }, + "inspector": { + "inputs": { + "contactId": { + "type": "text", + "tooltip": "The unique identifier of the contact to update.", + "label": "Contact ID", + "index": 0 + }, + "name": { + "type": "text", + "tooltip": "Contact name.", + "label": "Name", + "index": 1 + }, + "dayOfCycle": { + "type": "number", + "tooltip": "Day of cycle for autoresponders.", + "label": "Day of Cycle", + "index": 2 + }, + "campaignId": { + "type": "text", + "tooltip": "Target campaign ID.", + "label": "Campaign ID", + "index": 3 + }, + "note": { + "type": "textarea", + "tooltip": "Note about the contact.", + "label": "Note", + "index": 4 + }, + "scoring": { + "type": "number", + "tooltip": "Contact scoring value.", + "label": "Scoring", + "index": 5 + }, + "customFieldId": { + "type": "text", + "tooltip": "Custom field ID.", + "label": "Custom Field ID", + "index": 6 + }, + "customFieldValue": { + "type": "text", + "tooltip": "Value for the custom field.", + "label": "Custom Field Value", + "index": 7 + }, + "tagId": { + "type": "text", + "tooltip": "Tag ID to set.", + "label": "Tag ID", + "index": 8 + } + } + } + } + ], + "outPorts": [ + { + "name": "out" + } + ], + "icon": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI3LjguMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA4Ny45IDU2IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA4Ny45IDU2OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzAwQUJFOTt9Cgkuc3Qxe2ZpbGw6I0ZGRkZGRjt9Cjwvc3R5bGU+CjxnPgoJPGc+CgkJPHBhdGggY2xhc3M9InN0MCIgZD0iTTg3LjgsNTIuMWMwLDIuMS0xLjcsMy43LTMuNywzLjdIMy45Yy0yLjEsMC0zLjctMS43LTMuNy0zLjdWMy45YzAtMi4xLDEuNy0zLjcsMy43LTMuN0g4NAoJCQljMi4xLDAsMy43LDEuNywzLjcsMy43VjUyLjF6Ii8+Cgk8L2c+Cgk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODYuOSwxLjVDNzYuNiwxNy43LDYwLjUsMzIuNCw0My45LDMyLjFjLTYuMS0wLjEtMTMuMS0xLjktMTktNi43Yy00LjQtMy42LTguOC05LjItMTAuNS0xNy4yCgkJYy0wLjcsMC0xLjIsMC0xLjgsMGMtMi43LDAtNC4zLDIuNC00LDQuNmMwLjEsMC41LDAuMiwxLjEsMC4zLDEuN2MxLjMsNy44LDUuOSwxNi42LDEzLjYsMjIuM2M2LDQuNCwxMy40LDcuMywyMS41LDcuMwoJCWMxNC42LDAsMzAuMi05LjMsNDMuOC0zMy4yVjMuOUM4Ny44LDMsODcuNSwyLjIsODYuOSwxLjV6Ii8+CjwvZz4KPC9zdmc+Cg==" +} \ No newline at end of file diff --git a/src/appmixer/getResponse/core/UpdateTag/UpdateTag.js b/src/appmixer/getResponse/core/UpdateTag/UpdateTag.js new file mode 100644 index 0000000000..2efd7cae28 --- /dev/null +++ b/src/appmixer/getResponse/core/UpdateTag/UpdateTag.js @@ -0,0 +1,31 @@ +'use strict'; + +module.exports = { + async receive(context) { + const { tagId, name } = context.messages.in.content; + + if (!tagId) { + throw new context.CancelError('Tag ID is required!'); + } + + if (!name) { + throw new context.CancelError('Name is required!'); + } + + // https://apireference.getresponse.com/#tags + const { data } = await context.httpRequest({ + method: 'POST', + url: `https://api.getresponse.com/v3/tags/${tagId}`, + headers: { + 'X-Auth-Token': `api-key ${context.auth.apiKey}`, + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + data: { + name + } + }); + + return context.sendJson(data, 'out'); + } +}; diff --git a/src/appmixer/getResponse/core/UpdateTag/component.json b/src/appmixer/getResponse/core/UpdateTag/component.json new file mode 100644 index 0000000000..6227d877a2 --- /dev/null +++ b/src/appmixer/getResponse/core/UpdateTag/component.json @@ -0,0 +1,82 @@ +{ + "name": "appmixer.getResponse.core.UpdateTag", + "label": "Update Tag", + "author": "Appmixer ", + "description": "Update an existing tag by ID (e.g., rename).", + "version": "1.0.0", + "private": false, + "auth": { + "service": "appmixer:getResponse" + }, + "quota": { + "manager": "appmixer:getResponse", + "resources": "requests" + }, + "inPorts": [ + { + "name": "in", + "schema": { + "type": "object", + "properties": { + "tagId": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "tagId", + "name" + ] + }, + "inspector": { + "inputs": { + "tagId": { + "type": "text", + "label": "Tag ID", + "tooltip": "The unique identifier of the tag to update.", + "index": 0 + }, + "name": { + "type": "text", + "label": "Tag Name", + "tooltip": "New tag name.", + "index": 1 + } + } + } + } + ], + "outPorts": [ + { + "name": "out", + "schema": { + "type": "object", + "properties": { + "tagId": { + "type": "string", + "title": "Tag ID" + }, + "name": { + "type": "string", + "title": "Name" + }, + "href": { + "type": "string", + "title": "URL" + }, + "color": { + "type": "string", + "title": "Color" + }, + "createdAt": { + "type": "string", + "title": "Created At" + } + } + } + } + ], + "icon": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI3LjguMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA4Ny45IDU2IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA4Ny45IDU2OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+Cgkuc3Qwe2ZpbGw6IzAwQUJFOTt9Cgkuc3Qxe2ZpbGw6I0ZGRkZGRjt9Cjwvc3R5bGU+CjxnPgoJPGc+CgkJPHBhdGggY2xhc3M9InN0MCIgZD0iTTg3LjgsNTIuMWMwLDIuMS0xLjcsMy43LTMuNywzLjdIMy45Yy0yLjEsMC0zLjctMS43LTMuNy0zLjdWMy45YzAtMi4xLDEuNy0zLjcsMy43LTMuN0g4NAoJCQljMi4xLDAsMy43LDEuNywzLjcsMy43VjUyLjF6Ii8+Cgk8L2c+Cgk8cGF0aCBjbGFzcz0ic3QxIiBkPSJNODYuOSwxLjVDNzYuNiwxNy43LDYwLjUsMzIuNCw0My45LDMyLjFjLTYuMS0wLjEtMTMuMS0xLjktMTktNi43Yy00LjQtMy42LTguOC05LjItMTAuNS0xNy4yCgkJYy0wLjcsMC0xLjIsMC0xLjgsMGMtMi43LDAtNC4zLDIuNC00LDQuNmMwLjEsMC41LDAuMiwxLjEsMC4zLDEuN2MxLjMsNy44LDUuOSwxNi42LDEzLjYsMjIuM2M2LDQuNCwxMy40LDcuMywyMS41LDcuMwoJCWMxNC42LDAsMzAuMi05LjMsNDMuOC0zMy4yVjMuOUM4Ny44LDMsODcuNSwyLjIsODYuOSwxLjV6Ii8+CjwvZz4KPC9zdmc+Cg==" +} \ No newline at end of file diff --git a/src/appmixer/getResponse/lib.js b/src/appmixer/getResponse/lib.js new file mode 100644 index 0000000000..fe1bb963f6 --- /dev/null +++ b/src/appmixer/getResponse/lib.js @@ -0,0 +1,128 @@ +const pathModule = require('path'); + +const DEFAULT_PREFIX = 'getResponse-objects-export'; + +module.exports = { + + async sendArrayOutput({ + context, + outputPortName = 'out', + outputType = 'array', + records = [] + }) { + + if (outputType === 'first') { + if (records.length === 0) { + throw new context.CancelError('No records available for first output type'); + } + // Only the first record. + await context.sendJson( + { ...records[0], index: 0, count: records.length }, + outputPortName + ); + } else if (outputType === 'object') { + // One by one. + for (let index = 0; index < records.length; index++) { + await context.sendJson( + { ...records[index], index, count: records.length }, + outputPortName + ); + } + } else if (outputType === 'array') { + // All at once. + await context.sendJson({ result: records, count: records.length }, outputPortName); + } else if (outputType === 'file') { + + // Into CSV file. + const csvString = toCsv(records); + + let buffer = Buffer.from(csvString, 'utf8'); + const componentName = context.flowDescriptor[context.componentId].label || context.componentId; + const fileName = `${context.config.outputFilePrefix || DEFAULT_PREFIX}-${componentName}.csv`; + const savedFile = await context.saveFileStream(pathModule.normalize(fileName), buffer); + + await context.log({ step: 'File was saved', fileName, fileId: savedFile.fileId }); + await context.sendJson({ fileId: savedFile.fileId }, outputPortName); + } else { + throw new context.CancelError('Unsupported outputType ' + outputType); + } + }, + + getProperty(obj, path) { + return path.split('.').reduce((acc, part) => acc?.[part], obj); + }, + + getOutputPortOptions(context, outputType, itemSchema, { label }) { + + if (outputType === 'object' || outputType === 'first') { + const options = Object.keys(itemSchema) + .reduce((res, field) => { + const schema = itemSchema[field]; + const { title: label, ...schemaWithoutTitle } = schema; + + res.push({ + label, value: field, schema: schemaWithoutTitle + }); + return res; + }, [{ + label: 'Current Item Index', + value: 'index', + schema: { type: 'integer' } + }, { + label: 'Items Count', + value: 'count', + schema: { type: 'integer' } + }]); + + return context.sendJson(options, 'out'); + } + + if (outputType === 'array') { + return context.sendJson([{ + label: 'Items Count', + value: 'count', + schema: { type: 'integer' } + }, { + label: label, + value: 'result', + schema: { + type: 'array', + items: { + type: 'object', + properties: itemSchema + } + } + }], 'out'); + } + + if (outputType === 'file') { + return context.sendJson([{ label: 'File ID', value: 'fileId' }], 'out'); + } + } +}; + +/** + * @param {array} array + * @returns {string} + */ +const toCsv = (array) => { + + if (!array || array.length === 0) { + return ''; + } + + const headers = Object.keys(array[0]); + + return [ + headers.join(','), + ...array.map(items => { + + return Object.values(items).map(property => { + if (typeof property === 'object') { + return JSON.stringify(property); + } + return property; + }).join(','); + }) + ].join('\n'); +}; diff --git a/src/appmixer/getResponse/quota.js b/src/appmixer/getResponse/quota.js new file mode 100644 index 0000000000..f242f4245e --- /dev/null +++ b/src/appmixer/getResponse/quota.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = { + + rules: [ + { + limit: 5, + window: 1000, + queueing: 'fifo', + resource: 'requests' + } + ] +}; diff --git a/src/appmixer/getResponse/service.json b/src/appmixer/getResponse/service.json new file mode 100644 index 0000000000..00833ea9c1 --- /dev/null +++ b/src/appmixer/getResponse/service.json @@ -0,0 +1,8 @@ +{ + "name": "appmixer.getResponse", + "label": "GetResponse", + "category": "applications", + "description": "Email marketing platform. Manage contacts and tags using the GetResponse REST API v3.", + "version": "1.0.0", + "icon": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIGlkPSJ1dWlkLTE3NzFlNzg5LTcwMGUtNDk0Ny1hNzBhLWMzNjI1ZGM3MmRiZiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMTUwIDE5LjkxIj48ZyBpZD0idXVpZC03ZDc5NjBlZi01ZmRmLTQyZTEtYWFiZi0zMGIyNzQxMzdkYjciPjxwYXRoIGQ9Ik0xNC43NywxNC43N2MtMi43MywwLTUuMjQtLjk4LTcuMjYtMi40Ny0yLjYtMS45MS00LjE3LTQuODgtNC42LTcuNTEtLjAzLS4yLS4wNi0uMzktLjA5LS41Ni0uMTEtLjc0LjQzLTEuNTQsMS4zNC0xLjU0aC42MmMuNTcsMi42OSwyLjA1LDQuNTksMy41NCw1LjgxLDIsMS42Miw0LjM0LDIuMjIsNi40LDIuMjcsNS41OS4xMiwxMS4wNC00Ljg1LDE0LjUyLTEwLjI4LDAsMCwwLDAsMCwwLS4yMy0uMjktLjU4LS40Ny0uOTgtLjQ3SDEuMjZDLjU2LDAsMCwuNTcsMCwxLjI2djE2LjI3YzAsLjcuNTYsMS4yNiwxLjI2LDEuMjZoMjcuMDFjLjcsMCwxLjI2LS41NywxLjI2LTEuMjZWMy41OWMtNC41OCw4LjA2LTkuODUsMTEuMTctMTQuNzcsMTEuMThaIiBmaWxsPSIjMDBhMmZmIiBzdHJva2Utd2lkdGg9IjAiLz48cGF0aCBkPSJNMTQuNzMsMTAuNzZjLTIuMDYtLjA0LTQuNC0uNjQtNi40LTIuMjctMS40OS0xLjIxLTIuOTgtMy4xMi0zLjU0LTUuODFoLS42MmMtLjkxLDAtMS40NS44LTEuMzQsMS41NC4wMi4xNy4wNi4zNi4wOS41Ni40MiwyLjYzLDEuOTksNS42LDQuNiw3LjUxLDIuMDIsMS40OSw0LjUzLDIuNDcsNy4yNiwyLjQ3LDQuOTItLjAxLDEwLjE4LTMuMTIsMTQuNzctMTEuMThoMFYxLjI2YzAtLjMtLjExLS41OC0uMjgtLjc5LDAsMCwwLDAsMCwwLTMuNDgsNS40NC04LjkzLDEwLjQtMTQuNTIsMTAuMjhaIiBmaWxsPSIjZmZmIiBzdHJva2Utd2lkdGg9IjAiLz48cGF0aCBkPSJNMTQ1LjczLDYuMjZjMi43NiwwLDQuMjcsMi4wMiw0LjI3LDQuNTgsMCwuMjgtLjA2Ljg5LS4wNi44OWgtNi42NmMuMTksMS43LDEuNDQsMi41OSwyLjg5LDIuNTksMS41NSwwLDIuNy0xLjA4LDIuNy0xLjA4bDEsMS42NnMtMS40OCwxLjQ2LTMuODgsMS40NmMtMy4yLDAtNS4xOC0yLjMxLTUuMTgtNS4wNSwwLTIuOTcsMi01LjA1LDQuOS01LjA1Wk0xNDcuNTQsMTAuMDZjLS4wNC0xLjE1LS44My0xLjk1LTEuODItMS45NS0xLjIzLDAtMi4xLjc0LTIuMzYsMS45NWg0LjE4WiIgZmlsbD0iIzIwMjczMCIgc3Ryb2tlLXdpZHRoPSIwIi8+PHBhdGggZD0iTTU1LjA1LDYuMjZjMi43NiwwLDQuMjcsMi4wMiw0LjI3LDQuNTgsMCwuMjgtLjA2Ljg5LS4wNi44OWgtNi42NWMuMTksMS43LDEuNDQsMi41OSwyLjg5LDIuNTksMS41NSwwLDIuNy0xLjA4LDIuNy0xLjA4bDEsMS42NnMtMS40NywxLjQ2LTMuODcsMS40NmMtMy4yLDAtNS4xOC0yLjMxLTUuMTgtNS4wNSwwLTIuOTcsMi01LjA1LDQuOS01LjA1Wk01Ni44NywxMC4wNmMtLjA0LTEuMTUtLjgzLTEuOTUtMS44MS0xLjk1LTEuMjMsMC0yLjEuNzQtMi4zNiwxLjk1aDQuMThaIiBmaWxsPSIjMjAyNzMwIiBzdHJva2Utd2lkdGg9IjAiLz48cGF0aCBkPSJNODIuODIsNi4yNmMyLjc2LDAsNC4yNywyLjAyLDQuMjcsNC41OCwwLC4yOC0uMDYuODktLjA2Ljg5aC02LjY2Yy4xOSwxLjcsMS40NCwyLjU5LDIuODksMi41OSwxLjU1LDAsMi43MS0xLjA4LDIuNzEtMS4wOGwxLDEuNjZzLTEuNDcsMS40Ni0zLjg4LDEuNDZjLTMuMTksMC01LjE4LTIuMzEtNS4xOC01LjA1LDAtMi45NywyLTUuMDUsNC45LTUuMDVaTTg0LjYzLDEwLjA2Yy0uMDQtMS4xNS0uODMtMS45NS0xLjgxLTEuOTUtMS4yMywwLTIuMS43NC0yLjM2LDEuOTVoNC4xOFoiIGZpbGw9IiMyMDI3MzAiIHN0cm9rZS13aWR0aD0iMCIvPjxwYXRoIGQ9Ik0xMjEuMTUsOC45NGMwLS4yOC0uMTUtLjQyLS40MS0uNDJoLS43NnYtMi4wNGgyLjIxYy44NSwwLDEuMjcuNCwxLjI3LDEuMDh2LjI4YzAsLjIxLS4wNC40Mi0uMDQuNDJoLjA0Yy40Mi0uODEsMS40Ni0yLDMuNDEtMiwyLjE0LDAsMy4zNywxLjEyLDMuMzcsMy42N3YzLjc2YzAsLjI3LjE1LjQyLjQxLjQyaC43NnYyLjAyaC0yLjI5Yy0uOTEsMC0xLjI4LS4zOC0xLjI4LTEuMjl2LTQuNDNjMC0xLjE3LS4zLTEuOTctMS41MS0xLjk3LTEuMjksMC0yLjI1LjgxLTIuNTksMS45Ny0uMTMuNC0uMTkuODMtLjE5LDEuMjl2NC40MmgtMi4zOHYtNy4xOVoiIGZpbGw9IiMyMDI3MzAiIHN0cm9rZS13aWR0aD0iMCIvPjxwYXRoIGQ9Ik0xMTQuMDgsNi4yNmMyLjk1LDAsNS4zLDIuMSw1LjMsNS4wNXMtMi4zNSw1LjA1LTUuMyw1LjA1LTUuMjgtMi4wOC01LjI4LTUuMDUsMi4zNS01LjA1LDUuMjgtNS4wNVpNMTE0LjA4LDE0LjMxYzEuNTcsMCwyLjg4LTEuMjEsMi44OC0zLjAxcy0xLjMtMy4wMS0yLjg4LTMuMDEtMi44NSwxLjIzLTIuODUsMy4wMSwxLjMsMy4wMSwyLjg1LDMuMDFaIiBmaWxsPSIjMjAyNzMwIiBzdHJva2Utd2lkdGg9IjAiLz48cGF0aCBkPSJNMTA3LjY4LDExLjMxYzAsMi45OS0xLjY2LDUuMDUtNC4yNyw1LjA1LTIuMDgsMC0yLjg5LTEuMzYtMi44OS0xLjM2aC0uMDRzLjA0LjM0LjA0LjgzdjQuMDhoLTIuNHYtMTAuOTdjMC0uMjgtLjE1LS40Mi0uNDItLjQyaC0uNzZ2LTIuMDRoMi4xMmMuODcsMCwxLjIzLjM2LDEuMjMuODV2LjM0aC4wNHMuOC0xLjQyLDIuOTktMS40MmMyLjU1LDAsNC4zNywxLjk5LDQuMzcsNS4wNVpNMTAyLjg1LDE0LjMzYzEuNDIsMCwyLjQtMS4xOSwyLjQtMy4wM3MtMS4xMi0yLjk5LTIuNDItMi45OWMtMS42MSwwLTIuNCwxLjQ3LTIuNCwyLjk3LDAsMi4xNCwxLjE3LDMuMDQsMi40MiwzLjA0WiIgZmlsbD0iIzIwMjczMCIgc3Ryb2tlLXdpZHRoPSIwIi8+PHBhdGggZD0iTTg5LjQ3LDEzLjA3czEuMjMsMS40MiwyLjg3LDEuNDJjLjc0LDAsMS4zMS0uMywxLjMxLS45NCwwLTEuMzYtNS4wOS0xLjM0LTUuMDktNC40NiwwLTEuOTMsMS43NC0yLjgyLDMuNzUtMi44MiwxLjMsMCwzLjM4LjQzLDMuMzgsMi4wMXYxaC0yLjEydi0uNDdjMC0uNDUtLjY4LS42OC0xLjIxLS42OC0uODUsMC0xLjQ0LjMtMS40NC44NywwLDEuNTEsNS4xMiwxLjIxLDUuMTIsNC40MiwwLDEuODItMS42MSwyLjk1LTMuNzEsMi45NS0yLjY1LDAtNC4wMS0xLjcyLTQuMDEtMS43MmwxLjEzLTEuNTdaIiBmaWxsPSIjMjAyNzMwIiBzdHJva2Utd2lkdGg9IjAiLz48cGF0aCBkPSJNMTMzLjM5LDEzLjA3czEuMjMsMS40MiwyLjg3LDEuNDJjLjc0LDAsMS4zMS0uMywxLjMxLS45NCwwLTEuMzYtNS4wOS0xLjM0LTUuMDktNC40NiwwLTEuOTMsMS43NC0yLjgyLDMuNzQtMi44MiwxLjMsMCwzLjM4LjQzLDMuMzgsMi4wMXYxaC0yLjEydi0uNDdjMC0uNDUtLjY4LS42OC0xLjIxLS42OC0uODUsMC0xLjQ0LjMtMS40NC44NywwLDEuNTEsNS4xMiwxLjIxLDUuMTIsNC40MiwwLDEuODItMS42MSwyLjk1LTMuNywyLjk1LTIuNjUsMC00LjAxLTEuNzItNC4wMS0xLjcybDEuMTMtMS41N1oiIGZpbGw9IiMyMDI3MzAiIHN0cm9rZS13aWR0aD0iMCIvPjxwYXRoIGQ9Ik02NS42NSwxNC4xMWMtLjY2LDAtMS45MS0uMjMtMS45MS0xLjgydi0zLjloMi4xOXYtLjg5YzAtLjcxLS4zMi0xLjAyLTEuMDItMS4wMmgtMS4xN3YtMi42M2gtMi4zNHYyLjYzaC0xLjMxdjEuOTFoMS4yNXY0LjJjMCwzLjIzLDIuNjcsMy42Myw0LjAxLDMuNjMuNDQsMCwuNzQtLjA2Ljc0LS4wNnYtMi4xcy0uMTcuMDQtLjQzLjA0WiIgZmlsbD0iIzIwMjczMCIgc3Ryb2tlLXdpZHRoPSIwIi8+PHBhdGggZD0iTTQzLjA4LDIuNDdjMy4yOSwwLDQuOSwxLjcsNC45LDEuN2wtMS40LDEuNzhzLTEuNC0xLjE5LTMuNDQtMS4xOWMtMi4zMywwLTQuMzEsMS44NC00LjMxLDQuNTYsMCwyLjkzLDIsNC43Myw0LjQxLDQuNzMsMi4xNCwwLDMuNC0xLjQsMy40LTEuNHYtMWMwLS4yOC0uMTUtLjQyLS40Mi0uNDJoLS44MXYtMi4xaDIuMThjLjkxLDAsMS4yOS4zOCwxLjI5LDEuMjd2NS43M2gtMi4xMnYtLjY0YzAtLjI4LjAyLS41Ny4wMi0uNTdoLS4wNHMtMS40NCwxLjQ0LTMuOTUsMS40NGMtMy41MiwwLTYuNTMtMi42OS02LjUzLTcuMDIsMC0zLjg2LDIuOTMtNi44Nyw2LjgzLTYuODdaIiBmaWxsPSIjMjAyNzMwIiBzdHJva2Utd2lkdGg9IjAiLz48cGF0aCBkPSJNNjcuNDQsMi42OWg0Ljc4YzIuNDYsMCw0LjE4LDEuNTEsNC4xOCw0LjA1cy0xLjY2LDMuNDItMi4yMywzLjU0di4wNHMuNDIuMTkuNjYuNjhsMS4zMSwyLjU5Yy4yMS40LjUzLjQ0Ljk1LjQ0aC4yNHYyLjFoLTEuM2MtMSwwLTEuNDQtLjE1LTEuODctMWwtMS43Mi0zLjM5Yy0uMjgtLjUzLS41Ny0uNjItMS4yNy0uNjJoLTEuMjV2NS4wMWgtMi40OFYyLjY5Wk03MS44OCw4Ljk5YzEuMjUsMCwxLjk5LS43OCwxLjk5LTIuMTJzLS43NC0yLjA4LTEuOTUtMi4wOGgtMnY0LjJoMS45N1oiIGZpbGw9IiMyMDI3MzAiIHN0cm9rZS13aWR0aD0iMCIvPjwvZz48L3N2Zz4=" +} \ No newline at end of file