Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Configuration files for the [OpnForm](https://opnform.com) custom app on [Make.com](https://www.make.com) (formerly Integromat).

This app provides a **"Watch New Submissions"** instant trigger so Make users can automate workflows whenever a form receives a submission.
This app provides a **"Watch New Submissions"** instant trigger so Make users can automate workflows whenever a form receives a submission, plus the required **"Make an API Call"** universal module for authorized requests to the OpnForm API.

## How It Works

Expand Down Expand Up @@ -30,9 +30,10 @@ This integration follows the same lightweight pattern as OpnForm's Activepieces
│ ├── attach.json # Register webhook (POST /open/forms/{id}/integrations)
│ └── detach.json # Unregister webhook (DELETE /open/forms/{id}/integrations/{id})
├── modules/
│ └── watchNewSubmissions/
│ ├── communication.json # Empty: the paired webhook already produces the bundle
│ └── expect.json # Module parameters (form/workspace selectors)
│ ├── watchNewSubmissions/
│ │ ├── communication.json # Empty: the paired webhook already produces the bundle
│ │ └── expect.json # Empty: form/workspace selectors belong to the webhook
│ └── makeAnApiCall/ # Authorized REST universal module using relative API paths
└── rpcs/
├── listWorkspaces.json # Dynamic dropdown (GET /open/workspaces)
└── listForms.json # Dynamic dropdown (GET /open/workspaces/{id}/forms) with bounded pagination
Expand All @@ -48,6 +49,7 @@ All endpoints are part of OpnForm's existing API, authenticated via Bearer token
| `/open/workspaces/{id}/forms` | GET | List forms (paginated, up to 100/page) |
| `/open/forms/{id}/integrations` | POST | Create Make integration (attach webhook) |
| `/open/forms/{id}/integrations/{integrationId}` | DELETE | Remove integration (detach webhook) |
| Any relative path selected by the user | Any supported REST method | Universal authorized API call |

## Setup in Make

Expand Down
13 changes: 13 additions & 0 deletions makecomapp.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,19 @@
"interface": "modules/watchNewSubmissions/interface.json",
"samples": "modules/watchNewSubmissions/samples.json"
}
},
"makeAnApiCall": {
"label": "Make an API Call",
"description": "Performs an arbitrary authorized API call.",
"moduleType": "universal",
"connection": "apiKeyAuth",
"codeFiles": {
"communication": "modules/makeAnApiCall/communication.json",
"parameters": "modules/makeAnApiCall/parameters.json",
"mappableParams": "modules/makeAnApiCall/expect.json",
"interface": "modules/makeAnApiCall/interface.json",
"samples": "modules/makeAnApiCall/samples.json"
}
}
},
"rpc": {
Expand Down
19 changes: 19 additions & 0 deletions modules/makeAnApiCall/communication.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"qs": {
"{{...}}": "{{toCollection(parameters.qs, 'key', 'value')}}"
},
"url": "{{parameters.url}}",
"body": "{{parameters.body}}",
"type": "text",
"method": "{{parameters.method}}",
"headers": {
"{{...}}": "{{toCollection(parameters.headers, 'key', 'value')}}"
},
"response": {
"output": {
"body": "{{body}}",
"headers": "{{headers}}",
"statusCode": "{{statusCode}}"
}
}
}
84 changes: 84 additions & 0 deletions modules/makeAnApiCall/expect.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
[
{
"help": "Enter a path relative to the configured OpnForm API base URL. For example: /open/workspaces",
"name": "url",
"type": "text",
"label": "URL",
"required": true
},
{
"name": "method",
"type": "select",
"label": "Method",
"default": "GET",
"options": [
{
"label": "GET",
"value": "GET"
},
{
"label": "POST",
"value": "POST"
},
{
"label": "PUT",
"value": "PUT"
},
{
"label": "PATCH",
"value": "PATCH"
},
{
"label": "DELETE",
"value": "DELETE"
}
],
"required": true
},
{
"help": "You don't have to add authorization headers; we already did that for you.",
"name": "headers",
"spec": [
{
"name": "key",
"type": "text",
"label": "Key"
},
{
"name": "value",
"type": "text",
"label": "Value"
}
],
"type": "array",
"label": "Headers",
"default": [
{
"key": "Content-Type",
"value": "application/json"
}
]
},
{
"name": "qs",
"spec": [
{
"name": "key",
"type": "text",
"label": "Key"
},
{
"name": "value",
"type": "text",
"label": "Value"
}
],
"type": "array",
"label": "Query String"
},
{
"name": "body",
"type": "any",
"label": "Body"
}
]
17 changes: 17 additions & 0 deletions modules/makeAnApiCall/interface.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[
{
"name": "body",
"type": "any",
"label": "Body"
},
{
"name": "headers",
"type": "collection",
"label": "Headers"
},
{
"name": "statusCode",
"type": "number",
"label": "Status code"
}
]
1 change: 1 addition & 0 deletions modules/makeAnApiCall/parameters.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
1 change: 1 addition & 0 deletions modules/makeAnApiCall/samples.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
21 changes: 1 addition & 20 deletions modules/watchNewSubmissions/expect.json
Original file line number Diff line number Diff line change
@@ -1,20 +1 @@
[
{
"name": "workspaceId",
"type": "select",
"label": "Workspace",
"required": true,
"options": {
"store": "rpc://listWorkspaces",
"nested": [
{
"name": "formId",
"type": "select",
"label": "Form",
"required": true,
"options": "rpc://listForms"
}
]
}
}
]
[]
15 changes: 15 additions & 0 deletions tests/contracts.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ test('attached webhook persists and reuses the remote integration id', async ()

test('instant trigger preserves the bundle produced by its webhook', async () => {
const communication = await readJson('modules/watchNewSubmissions/communication.json')
const parameters = await readJson('modules/watchNewSubmissions/expect.json')

assert.deepEqual(communication, {})
assert.deepEqual(parameters, [])
})

test('form RPC follows the Laravel resource pagination envelope', async () => {
Expand Down Expand Up @@ -67,6 +69,19 @@ test('trigger interface exposes dynamic fields and optional edit link', async ()
assert.equal(editLink.type, 'url')
})

test('universal module only accepts relative API paths and uses the app connection', async () => {
const manifest = await readJson('makecomapp.json')
const universal = manifest.components.module.makeAnApiCall
const communication = await readJson('modules/makeAnApiCall/communication.json')
const parameters = await readJson('modules/makeAnApiCall/expect.json')

assert.equal(universal.moduleType, 'universal')
assert.equal(universal.connection, 'apiKeyAuth')
assert.equal(communication.url, '{{parameters.url}}')
assert.doesNotMatch(communication.url, /^https?:\/\//)
assert.match(parameters.find(({ name }) => name === 'url').help, /\/open\/workspaces/)
})

test('Make requests use relative open API paths inherited from Base', async () => {
const paths = [
'rpcs/listWorkspaces.json',
Expand Down
Loading