From 88c41083c151495195f6adb954a26e71c37f55cc Mon Sep 17 00:00:00 2001 From: Tim Wheeler <63284593+TimWheeler-SQUP@users.noreply.github.com> Date: Sat, 25 Apr 2026 20:11:50 +0100 Subject: [PATCH 01/21] New AutoTask plugin and data streams --- plugins/AutoTask/configValidation.json | 11 +++ plugins/AutoTask/custom_types.json | 37 ++++++++ plugins/AutoTask/dataStreams/companies.json | 21 +++++ plugins/AutoTask/dataStreams/contacts.json | 21 +++++ .../AutoTask/dataStreams/contractStatus.json | 30 +++++++ plugins/AutoTask/dataStreams/contracts.json | 21 +++++ .../AutoTask/dataStreams/financialHealth.json | 30 +++++++ .../AutoTask/dataStreams/projectStatus.json | 31 +++++++ plugins/AutoTask/dataStreams/projects.json | 21 +++++ .../dataStreams/resourceUtilisation.json | 30 +++++++ plugins/AutoTask/dataStreams/resources.json | 21 +++++ .../AutoTask/dataStreams/scripts/tickets.js | 15 ++++ plugins/AutoTask/dataStreams/tickets.json | 47 ++++++++++ plugins/AutoTask/icon.png | Bin 0 -> 13387 bytes .../AutoTask/indexDefinitions/default.json | 85 ++++++++++++++++++ plugins/AutoTask/metadata.json | 48 ++++++++++ plugins/AutoTask/ui.json | 40 +++++++++ 17 files changed, 509 insertions(+) create mode 100644 plugins/AutoTask/configValidation.json create mode 100644 plugins/AutoTask/custom_types.json create mode 100644 plugins/AutoTask/dataStreams/companies.json create mode 100644 plugins/AutoTask/dataStreams/contacts.json create mode 100644 plugins/AutoTask/dataStreams/contractStatus.json create mode 100644 plugins/AutoTask/dataStreams/contracts.json create mode 100644 plugins/AutoTask/dataStreams/financialHealth.json create mode 100644 plugins/AutoTask/dataStreams/projectStatus.json create mode 100644 plugins/AutoTask/dataStreams/projects.json create mode 100644 plugins/AutoTask/dataStreams/resourceUtilisation.json create mode 100644 plugins/AutoTask/dataStreams/resources.json create mode 100644 plugins/AutoTask/dataStreams/scripts/tickets.js create mode 100644 plugins/AutoTask/dataStreams/tickets.json create mode 100644 plugins/AutoTask/icon.png create mode 100644 plugins/AutoTask/indexDefinitions/default.json create mode 100644 plugins/AutoTask/metadata.json create mode 100644 plugins/AutoTask/ui.json diff --git a/plugins/AutoTask/configValidation.json b/plugins/AutoTask/configValidation.json new file mode 100644 index 0000000..c250fab --- /dev/null +++ b/plugins/AutoTask/configValidation.json @@ -0,0 +1,11 @@ +{ + "steps": [ + { + "displayName": "AutoTask connection", + "dataStream": { "name": "companies" }, + "success": "Successfully connected to AutoTask", + "error": "Cannot connect to AutoTask — check your Zone URL, Integration Code, Username and Secret", + "required": true + } + ] +} diff --git a/plugins/AutoTask/custom_types.json b/plugins/AutoTask/custom_types.json new file mode 100644 index 0000000..24c6635 --- /dev/null +++ b/plugins/AutoTask/custom_types.json @@ -0,0 +1,37 @@ +[ + { + "name": "AutoTask Company", + "sourceType": "autotask-company", + "icon": "building-2", + "singular": "Company", + "plural": "Companies" + }, + { + "name": "AutoTask Contact", + "sourceType": "autotask-contact", + "icon": "user", + "singular": "Contact", + "plural": "Contacts" + }, + { + "name": "AutoTask Project", + "sourceType": "autotask-project", + "icon": "folder-kanban", + "singular": "Project", + "plural": "Projects" + }, + { + "name": "AutoTask Resource", + "sourceType": "autotask-resource", + "icon": "hard-hat", + "singular": "Resource", + "plural": "Resources" + }, + { + "name": "AutoTask Contract", + "sourceType": "autotask-contract", + "icon": "file-text", + "singular": "Contract", + "plural": "Contracts" + } +] diff --git a/plugins/AutoTask/dataStreams/companies.json b/plugins/AutoTask/dataStreams/companies.json new file mode 100644 index 0000000..dba1606 --- /dev/null +++ b/plugins/AutoTask/dataStreams/companies.json @@ -0,0 +1,21 @@ +{ + "name": "companies", + "displayName": "Companies", + "baseDataSourceName": "httpRequestUnscoped", + "visibility": { "type": "hidden" }, + "timeframes": false, + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/Companies/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"exist\",\"field\":\"id\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + } +} diff --git a/plugins/AutoTask/dataStreams/contacts.json b/plugins/AutoTask/dataStreams/contacts.json new file mode 100644 index 0000000..d01971a --- /dev/null +++ b/plugins/AutoTask/dataStreams/contacts.json @@ -0,0 +1,21 @@ +{ + "name": "contacts", + "displayName": "Contacts", + "baseDataSourceName": "httpRequestUnscoped", + "visibility": { "type": "hidden" }, + "timeframes": false, + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/Contacts/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"exist\",\"field\":\"id\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + } +} diff --git a/plugins/AutoTask/dataStreams/contractStatus.json b/plugins/AutoTask/dataStreams/contractStatus.json new file mode 100644 index 0000000..15bc11a --- /dev/null +++ b/plugins/AutoTask/dataStreams/contractStatus.json @@ -0,0 +1,30 @@ +{ + "name": "contractStatus", + "displayName": "Contract Status", + "baseDataSourceName": "httpRequestUnscoped", + "timeframes": false, + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/Contracts/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"exist\",\"field\":\"id\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + }, + "metadata": [ + { "name": "id", "displayName": "ID", "visible": false }, + { "name": "contractName", "displayName": "Contract Name" }, + { "name": "contractType", "displayName": "Contract Type", "shape": ["number", { "decimalPlaces": 0 }] }, + { "name": "status", "displayName": "Status", "shape": ["number", { "decimalPlaces": 0 }] }, + { "name": "startDate", "displayName": "Start Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "endDate", "displayName": "End Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "companyID", "displayName": "Company ID" }, + { "pattern": ".*" } + ] +} diff --git a/plugins/AutoTask/dataStreams/contracts.json b/plugins/AutoTask/dataStreams/contracts.json new file mode 100644 index 0000000..76937bd --- /dev/null +++ b/plugins/AutoTask/dataStreams/contracts.json @@ -0,0 +1,21 @@ +{ + "name": "contracts", + "displayName": "Contracts", + "baseDataSourceName": "httpRequestUnscoped", + "visibility": { "type": "hidden" }, + "timeframes": false, + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/Contracts/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"exist\",\"field\":\"id\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + } +} diff --git a/plugins/AutoTask/dataStreams/financialHealth.json b/plugins/AutoTask/dataStreams/financialHealth.json new file mode 100644 index 0000000..caa3707 --- /dev/null +++ b/plugins/AutoTask/dataStreams/financialHealth.json @@ -0,0 +1,30 @@ +{ + "name": "financialHealth", + "displayName": "Financial Health", + "baseDataSourceName": "httpRequestUnscoped", + "timeframes": false, + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/Contracts/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"exist\",\"field\":\"id\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + }, + "metadata": [ + { "name": "id", "displayName": "ID", "visible": false }, + { "name": "contractName", "displayName": "Contract Name" }, + { "name": "contractType", "displayName": "Contract Type", "shape": ["number", { "decimalPlaces": 0 }] }, + { "name": "estimatedRevenue", "displayName": "Estimated Revenue", "shape": ["number", { "decimalPlaces": 2 }] }, + { "name": "estimatedCost", "displayName": "Estimated Cost", "shape": ["number", { "decimalPlaces": 2 }] }, + { "name": "estimatedHours", "displayName": "Estimated Hours", "shape": ["number", { "decimalPlaces": 2 }] }, + { "name": "status", "displayName": "Status", "shape": ["number", { "decimalPlaces": 0 }] }, + { "pattern": ".*" } + ] +} diff --git a/plugins/AutoTask/dataStreams/projectStatus.json b/plugins/AutoTask/dataStreams/projectStatus.json new file mode 100644 index 0000000..6a1e5cd --- /dev/null +++ b/plugins/AutoTask/dataStreams/projectStatus.json @@ -0,0 +1,31 @@ +{ + "name": "projectStatus", + "displayName": "Project Status", + "baseDataSourceName": "httpRequestUnscoped", + "timeframes": false, + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/Projects/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"exist\",\"field\":\"id\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + }, + "metadata": [ + { "name": "id", "displayName": "ID", "visible": false }, + { "name": "projectName", "displayName": "Project Name" }, + { "name": "status", "displayName": "Status", "shape": ["number", { "decimalPlaces": 0 }] }, + { "name": "completedPercentage", "displayName": "Completed %", "shape": ["number", { "decimalPlaces": 0 }] }, + { "name": "actualHours", "displayName": "Actual Hours", "shape": ["number", { "decimalPlaces": 2 }] }, + { "name": "estimatedTime", "displayName": "Estimated Hours", "shape": ["number", { "decimalPlaces": 2 }] }, + { "name": "startDateTime", "displayName": "Start Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "endDateTime", "displayName": "End Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "pattern": ".*" } + ] +} diff --git a/plugins/AutoTask/dataStreams/projects.json b/plugins/AutoTask/dataStreams/projects.json new file mode 100644 index 0000000..3d5d322 --- /dev/null +++ b/plugins/AutoTask/dataStreams/projects.json @@ -0,0 +1,21 @@ +{ + "name": "projects", + "displayName": "Projects", + "baseDataSourceName": "httpRequestUnscoped", + "visibility": { "type": "hidden" }, + "timeframes": false, + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/Projects/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"exist\",\"field\":\"id\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + } +} diff --git a/plugins/AutoTask/dataStreams/resourceUtilisation.json b/plugins/AutoTask/dataStreams/resourceUtilisation.json new file mode 100644 index 0000000..a38f9c7 --- /dev/null +++ b/plugins/AutoTask/dataStreams/resourceUtilisation.json @@ -0,0 +1,30 @@ +{ + "name": "resourceUtilisation", + "displayName": "Resource Utilisation", + "baseDataSourceName": "httpRequestUnscoped", + "timeframes": ["last7days", "last30days", "thisMonth", "lastMonth", "thisQuarter", "lastQuarter", "thisYear"], + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/TimeEntries/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"gte\",\"field\":\"dateWorked\",\"value\":\"{{timeframe.start}}\"},{\"op\":\"lte\",\"field\":\"dateWorked\",\"value\":\"{{timeframe.end}}\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + }, + "metadata": [ + { "name": "id", "displayName": "ID", "visible": false }, + { "name": "resourceID", "displayName": "Resource ID" }, + { "name": "dateWorked", "displayName": "Date Worked", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "hoursWorked", "displayName": "Hours Worked", "shape": ["number", { "decimalPlaces": 2 }] }, + { "name": "hoursToBill", "displayName": "Hours to Bill", "shape": ["number", { "decimalPlaces": 2 }] }, + { "name": "billingCodeID", "displayName": "Billing Code ID" }, + { "name": "ticketID", "displayName": "Ticket ID" }, + { "pattern": ".*" } + ] +} diff --git a/plugins/AutoTask/dataStreams/resources.json b/plugins/AutoTask/dataStreams/resources.json new file mode 100644 index 0000000..72dc1c2 --- /dev/null +++ b/plugins/AutoTask/dataStreams/resources.json @@ -0,0 +1,21 @@ +{ + "name": "resources", + "displayName": "Resources", + "baseDataSourceName": "httpRequestUnscoped", + "visibility": { "type": "hidden" }, + "timeframes": false, + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/Resources/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"exist\",\"field\":\"id\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + } +} diff --git a/plugins/AutoTask/dataStreams/scripts/tickets.js b/plugins/AutoTask/dataStreams/scripts/tickets.js new file mode 100644 index 0000000..831759b --- /dev/null +++ b/plugins/AutoTask/dataStreams/scripts/tickets.js @@ -0,0 +1,15 @@ +var statusMap = { + 1: { name: 'New', state: 'warning' }, + 5: { name: 'Complete', state: 'success' }, + 8: { name: 'In Progress', state: 'warning' }, + 9: { name: 'Waiting Customer', state: 'unknown' }, + 10: { name: 'Waiting Materials', state: 'unknown' }, + 11: { name: 'Waiting Vendor', state: 'unknown' }, + 12: { name: 'Escalate', state: 'error' }, + 13: { name: 'Waiting Approval', state: 'unknown' } +}; + +result = (data.items || []).map(function(ticket) { + var mapped = statusMap[ticket.status] || { name: 'Status ' + ticket.status, state: 'unknown' }; + return Object.assign({}, ticket, { statusName: mapped.name, stateValue: mapped.state }); +}); diff --git a/plugins/AutoTask/dataStreams/tickets.json b/plugins/AutoTask/dataStreams/tickets.json new file mode 100644 index 0000000..872f54e --- /dev/null +++ b/plugins/AutoTask/dataStreams/tickets.json @@ -0,0 +1,47 @@ +{ + "name": "tickets", + "displayName": "Tickets", + "baseDataSourceName": "httpRequestUnscoped", + "timeframes": ["last24hours", "last7days", "last30days", "thisMonth", "lastMonth", "thisQuarter", "lastQuarter", "thisYear"], + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/Tickets/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"gte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.start}}\"},{\"op\":\"lte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.end}}\"}]}", + "pathToData": "", + "postRequestScript": "scripts/tickets.js", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + }, + "metadata": [ + { "name": "id", "displayName": "Ticket ID", "shape": "string" }, + { "name": "title", "displayName": "Title" }, + { + "name": "statusName", + "displayName": "Status", + "shape": ["state", { + "map": { + "success": ["Complete"], + "error": ["Escalate"], + "warning": ["New", "In Progress"], + "unknown": ["Waiting Customer", "Waiting Materials", "Waiting Vendor", "Waiting Approval"] + } + }] + }, + { "name": "priority", "displayName": "Priority", "shape": ["number", { "decimalPlaces": 0 }] }, + { "name": "companyID", "sourceType": "autotask-company", "visible": false }, + { "name": "companyName", "displayName": "Company", "sourceId": "companyID", "objectPropertyPath": "name" }, + { "name": "contactID", "sourceType": "autotask-contact", "visible": false }, + { "name": "contactName", "displayName": "Contact", "sourceId": "contactID", "objectPropertyPath": "name" }, + { "name": "assignedResourceID", "sourceType": "autotask-resource", "visible": false }, + { "name": "assignedResourceName", "displayName": "Assigned Resource", "sourceId": "assignedResourceID", "objectPropertyPath": "name" }, + { "name": "lastActivityDate", "displayName": "Last Activity Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "dueDateTime", "displayName": "Due Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "pattern": ".*" } + ] +} diff --git a/plugins/AutoTask/icon.png b/plugins/AutoTask/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ccee9663d65d9ef8ecb6b3fde0b324a531f68572 GIT binary patch literal 13387 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 Resources > API User", + "validation": { + "required": true + } + }, + { + "type": "text", + "name": "userName", + "label": "API Username", + "placeholder": "apiuser@yourdomain.com", + "help": "The email address of your AutoTask API User", + "validation": { + "required": true + } + }, + { + "type": "password", + "name": "secret", + "label": "API Secret", + "help": "Generated when creating an API User in AutoTask", + "validation": { + "required": true + } + } +] From b2687e6613495fea3375e34220e4a168b2a23a07 Mon Sep 17 00:00:00 2001 From: Tim Wheeler <63284593+TimWheeler-SQUP@users.noreply.github.com> Date: Mon, 27 Apr 2026 12:09:00 +0100 Subject: [PATCH 02/21] Update folder --- plugins/AutoTask/{ => v1}/configValidation.json | 0 plugins/AutoTask/{ => v1}/custom_types.json | 0 .../AutoTask/{ => v1}/dataStreams/companies.json | 0 plugins/AutoTask/{ => v1}/dataStreams/contacts.json | 0 .../{ => v1}/dataStreams/contractStatus.json | 0 .../AutoTask/{ => v1}/dataStreams/contracts.json | 0 .../{ => v1}/dataStreams/financialHealth.json | 0 .../{ => v1}/dataStreams/projectStatus.json | 0 plugins/AutoTask/{ => v1}/dataStreams/projects.json | 0 .../{ => v1}/dataStreams/resourceUtilisation.json | 0 .../AutoTask/{ => v1}/dataStreams/resources.json | 0 .../{ => v1}/dataStreams/scripts/tickets.js | 0 plugins/AutoTask/{ => v1}/dataStreams/tickets.json | 0 plugins/AutoTask/{ => v1}/icon.png | Bin .../AutoTask/{ => v1}/indexDefinitions/default.json | 0 plugins/AutoTask/{ => v1}/metadata.json | 0 plugins/AutoTask/{ => v1}/ui.json | 0 17 files changed, 0 insertions(+), 0 deletions(-) rename plugins/AutoTask/{ => v1}/configValidation.json (100%) rename plugins/AutoTask/{ => v1}/custom_types.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/companies.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/contacts.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/contractStatus.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/contracts.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/financialHealth.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/projectStatus.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/projects.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/resourceUtilisation.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/resources.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/scripts/tickets.js (100%) rename plugins/AutoTask/{ => v1}/dataStreams/tickets.json (100%) rename plugins/AutoTask/{ => v1}/icon.png (100%) rename plugins/AutoTask/{ => v1}/indexDefinitions/default.json (100%) rename plugins/AutoTask/{ => v1}/metadata.json (100%) rename plugins/AutoTask/{ => v1}/ui.json (100%) diff --git a/plugins/AutoTask/configValidation.json b/plugins/AutoTask/v1/configValidation.json similarity index 100% rename from plugins/AutoTask/configValidation.json rename to plugins/AutoTask/v1/configValidation.json diff --git a/plugins/AutoTask/custom_types.json b/plugins/AutoTask/v1/custom_types.json similarity index 100% rename from plugins/AutoTask/custom_types.json rename to plugins/AutoTask/v1/custom_types.json diff --git a/plugins/AutoTask/dataStreams/companies.json b/plugins/AutoTask/v1/dataStreams/companies.json similarity index 100% rename from plugins/AutoTask/dataStreams/companies.json rename to plugins/AutoTask/v1/dataStreams/companies.json diff --git a/plugins/AutoTask/dataStreams/contacts.json b/plugins/AutoTask/v1/dataStreams/contacts.json similarity index 100% rename from plugins/AutoTask/dataStreams/contacts.json rename to plugins/AutoTask/v1/dataStreams/contacts.json diff --git a/plugins/AutoTask/dataStreams/contractStatus.json b/plugins/AutoTask/v1/dataStreams/contractStatus.json similarity index 100% rename from plugins/AutoTask/dataStreams/contractStatus.json rename to plugins/AutoTask/v1/dataStreams/contractStatus.json diff --git a/plugins/AutoTask/dataStreams/contracts.json b/plugins/AutoTask/v1/dataStreams/contracts.json similarity index 100% rename from plugins/AutoTask/dataStreams/contracts.json rename to plugins/AutoTask/v1/dataStreams/contracts.json diff --git a/plugins/AutoTask/dataStreams/financialHealth.json b/plugins/AutoTask/v1/dataStreams/financialHealth.json similarity index 100% rename from plugins/AutoTask/dataStreams/financialHealth.json rename to plugins/AutoTask/v1/dataStreams/financialHealth.json diff --git a/plugins/AutoTask/dataStreams/projectStatus.json b/plugins/AutoTask/v1/dataStreams/projectStatus.json similarity index 100% rename from plugins/AutoTask/dataStreams/projectStatus.json rename to plugins/AutoTask/v1/dataStreams/projectStatus.json diff --git a/plugins/AutoTask/dataStreams/projects.json b/plugins/AutoTask/v1/dataStreams/projects.json similarity index 100% rename from plugins/AutoTask/dataStreams/projects.json rename to plugins/AutoTask/v1/dataStreams/projects.json diff --git a/plugins/AutoTask/dataStreams/resourceUtilisation.json b/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json similarity index 100% rename from plugins/AutoTask/dataStreams/resourceUtilisation.json rename to plugins/AutoTask/v1/dataStreams/resourceUtilisation.json diff --git a/plugins/AutoTask/dataStreams/resources.json b/plugins/AutoTask/v1/dataStreams/resources.json similarity index 100% rename from plugins/AutoTask/dataStreams/resources.json rename to plugins/AutoTask/v1/dataStreams/resources.json diff --git a/plugins/AutoTask/dataStreams/scripts/tickets.js b/plugins/AutoTask/v1/dataStreams/scripts/tickets.js similarity index 100% rename from plugins/AutoTask/dataStreams/scripts/tickets.js rename to plugins/AutoTask/v1/dataStreams/scripts/tickets.js diff --git a/plugins/AutoTask/dataStreams/tickets.json b/plugins/AutoTask/v1/dataStreams/tickets.json similarity index 100% rename from plugins/AutoTask/dataStreams/tickets.json rename to plugins/AutoTask/v1/dataStreams/tickets.json diff --git a/plugins/AutoTask/icon.png b/plugins/AutoTask/v1/icon.png similarity index 100% rename from plugins/AutoTask/icon.png rename to plugins/AutoTask/v1/icon.png diff --git a/plugins/AutoTask/indexDefinitions/default.json b/plugins/AutoTask/v1/indexDefinitions/default.json similarity index 100% rename from plugins/AutoTask/indexDefinitions/default.json rename to plugins/AutoTask/v1/indexDefinitions/default.json diff --git a/plugins/AutoTask/metadata.json b/plugins/AutoTask/v1/metadata.json similarity index 100% rename from plugins/AutoTask/metadata.json rename to plugins/AutoTask/v1/metadata.json diff --git a/plugins/AutoTask/ui.json b/plugins/AutoTask/v1/ui.json similarity index 100% rename from plugins/AutoTask/ui.json rename to plugins/AutoTask/v1/ui.json From 878e49e2757fb91dc3fc26931c331f93488f967e Mon Sep 17 00:00:00 2001 From: Tim Wheeler <63284593+TimWheeler-SQUP@users.noreply.github.com> Date: Tue, 28 Apr 2026 07:47:12 +0100 Subject: [PATCH 03/21] Fix paging --- plugins/AutoTask/v1/configValidation.json | 2 +- plugins/AutoTask/v1/dataStreams/companies.json | 5 ++--- plugins/AutoTask/v1/dataStreams/contacts.json | 4 ++-- plugins/AutoTask/v1/dataStreams/contractStatus.json | 4 ++-- plugins/AutoTask/v1/dataStreams/contracts.json | 4 ++-- plugins/AutoTask/v1/dataStreams/financialHealth.json | 4 ++-- plugins/AutoTask/v1/dataStreams/projectStatus.json | 4 ++-- plugins/AutoTask/v1/dataStreams/projects.json | 4 ++-- plugins/AutoTask/v1/dataStreams/resourceUtilisation.json | 4 ++-- plugins/AutoTask/v1/dataStreams/resources.json | 4 ++-- plugins/AutoTask/v1/dataStreams/tickets.json | 5 ++--- 11 files changed, 21 insertions(+), 23 deletions(-) diff --git a/plugins/AutoTask/v1/configValidation.json b/plugins/AutoTask/v1/configValidation.json index c250fab..3212a51 100644 --- a/plugins/AutoTask/v1/configValidation.json +++ b/plugins/AutoTask/v1/configValidation.json @@ -2,7 +2,7 @@ "steps": [ { "displayName": "AutoTask connection", - "dataStream": { "name": "companies" }, + "dataStream": { "name": "companies", "timeframe": "none" }, "success": "Successfully connected to AutoTask", "error": "Cannot connect to AutoTask — check your Zone URL, Integration Code, Username and Secret", "required": true diff --git a/plugins/AutoTask/v1/dataStreams/companies.json b/plugins/AutoTask/v1/dataStreams/companies.json index dba1606..d493518 100644 --- a/plugins/AutoTask/v1/dataStreams/companies.json +++ b/plugins/AutoTask/v1/dataStreams/companies.json @@ -2,7 +2,6 @@ "name": "companies", "displayName": "Companies", "baseDataSourceName": "httpRequestUnscoped", - "visibility": { "type": "hidden" }, "timeframes": false, "config": { "httpMethod": "get", @@ -11,9 +10,9 @@ "pathToData": "items", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } diff --git a/plugins/AutoTask/v1/dataStreams/contacts.json b/plugins/AutoTask/v1/dataStreams/contacts.json index d01971a..5bd1e41 100644 --- a/plugins/AutoTask/v1/dataStreams/contacts.json +++ b/plugins/AutoTask/v1/dataStreams/contacts.json @@ -11,9 +11,9 @@ "pathToData": "items", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } diff --git a/plugins/AutoTask/v1/dataStreams/contractStatus.json b/plugins/AutoTask/v1/dataStreams/contractStatus.json index 15bc11a..83e2c93 100644 --- a/plugins/AutoTask/v1/dataStreams/contractStatus.json +++ b/plugins/AutoTask/v1/dataStreams/contractStatus.json @@ -10,9 +10,9 @@ "pathToData": "items", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } diff --git a/plugins/AutoTask/v1/dataStreams/contracts.json b/plugins/AutoTask/v1/dataStreams/contracts.json index 76937bd..fe9ad48 100644 --- a/plugins/AutoTask/v1/dataStreams/contracts.json +++ b/plugins/AutoTask/v1/dataStreams/contracts.json @@ -11,9 +11,9 @@ "pathToData": "items", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } diff --git a/plugins/AutoTask/v1/dataStreams/financialHealth.json b/plugins/AutoTask/v1/dataStreams/financialHealth.json index caa3707..cb18e6a 100644 --- a/plugins/AutoTask/v1/dataStreams/financialHealth.json +++ b/plugins/AutoTask/v1/dataStreams/financialHealth.json @@ -10,9 +10,9 @@ "pathToData": "items", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } diff --git a/plugins/AutoTask/v1/dataStreams/projectStatus.json b/plugins/AutoTask/v1/dataStreams/projectStatus.json index 6a1e5cd..bebb1a2 100644 --- a/plugins/AutoTask/v1/dataStreams/projectStatus.json +++ b/plugins/AutoTask/v1/dataStreams/projectStatus.json @@ -10,9 +10,9 @@ "pathToData": "items", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } diff --git a/plugins/AutoTask/v1/dataStreams/projects.json b/plugins/AutoTask/v1/dataStreams/projects.json index 3d5d322..cfe895f 100644 --- a/plugins/AutoTask/v1/dataStreams/projects.json +++ b/plugins/AutoTask/v1/dataStreams/projects.json @@ -11,9 +11,9 @@ "pathToData": "items", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } diff --git a/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json b/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json index a38f9c7..85287a2 100644 --- a/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json +++ b/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json @@ -10,9 +10,9 @@ "pathToData": "items", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } diff --git a/plugins/AutoTask/v1/dataStreams/resources.json b/plugins/AutoTask/v1/dataStreams/resources.json index 72dc1c2..f0321e9 100644 --- a/plugins/AutoTask/v1/dataStreams/resources.json +++ b/plugins/AutoTask/v1/dataStreams/resources.json @@ -11,9 +11,9 @@ "pathToData": "items", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } diff --git a/plugins/AutoTask/v1/dataStreams/tickets.json b/plugins/AutoTask/v1/dataStreams/tickets.json index 872f54e..45edbad 100644 --- a/plugins/AutoTask/v1/dataStreams/tickets.json +++ b/plugins/AutoTask/v1/dataStreams/tickets.json @@ -7,13 +7,12 @@ "httpMethod": "get", "expandInnerObjects": true, "endpointPath": "atservicesrest/v1.0/Tickets/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"gte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.start}}\"},{\"op\":\"lte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.end}}\"}]}", - "pathToData": "", "postRequestScript": "scripts/tickets.js", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } From dc0265b34760af75199e481bb0abaf7fb9b44a36 Mon Sep 17 00:00:00 2001 From: Tim Wheeler <63284593+TimWheeler-SQUP@users.noreply.github.com> Date: Wed, 29 Apr 2026 17:54:13 +0100 Subject: [PATCH 04/21] update icon --- plugins/AutoTask/v1/icon.png | Bin 13387 -> 7405 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/plugins/AutoTask/v1/icon.png b/plugins/AutoTask/v1/icon.png index ccee9663d65d9ef8ecb6b3fde0b324a531f68572..c64f4383e75ab8062c973f1383136bb7c0c75abc 100644 GIT binary patch literal 7405 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 Date: Thu, 30 Apr 2026 09:14:57 +0100 Subject: [PATCH 05/21] remove js file and updated status field --- CLAUDE.md | 23 +++++++++++++++++++ plugins/AutoTask/v1/dataStreams/contacts.json | 6 ++++- .../v1/dataStreams/projectStatus.json | 2 +- plugins/AutoTask/v1/dataStreams/tickets.json | 6 +++-- 4 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8ff872c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,23 @@ +# Plugin Authoring Guidelines + +## Status mappings + +Where an API returns a numeric or coded status value that needs mapping to a human-readable label and a SquaredUp state, implement the mapping directly in the data stream `.json` file using a `computed` column with a `valueExpression` ternary chain. Only use a `.js` post-request script when the mapping logic is too complex for an expression (e.g. requires lookups across multiple fields or external data). + +Example: + +```json +{ + "name": "statusName", + "displayName": "Status", + "computed": true, + "valueExpression": "{{ $['status'] == 1 ? 'Open' : $['status'] == 2 ? 'Closed' : 'Unknown' }}", + "shape": ["state", { + "map": { + "warning": ["Open"], + "success": ["Closed"], + "unknown": ["Unknown"] + } + }] +} +``` diff --git a/plugins/AutoTask/v1/dataStreams/contacts.json b/plugins/AutoTask/v1/dataStreams/contacts.json index 5bd1e41..97c73f2 100644 --- a/plugins/AutoTask/v1/dataStreams/contacts.json +++ b/plugins/AutoTask/v1/dataStreams/contacts.json @@ -17,5 +17,9 @@ "path": "pageDetails.nextPageUrl" } } - } + }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape":"string"}, + { "pattern": ".*" } + ] } diff --git a/plugins/AutoTask/v1/dataStreams/projectStatus.json b/plugins/AutoTask/v1/dataStreams/projectStatus.json index bebb1a2..9c726f8 100644 --- a/plugins/AutoTask/v1/dataStreams/projectStatus.json +++ b/plugins/AutoTask/v1/dataStreams/projectStatus.json @@ -18,7 +18,7 @@ } }, "metadata": [ - { "name": "id", "displayName": "ID", "visible": false }, + { "name": "id", "displayName": "ID", "shape": "string" }, { "name": "projectName", "displayName": "Project Name" }, { "name": "status", "displayName": "Status", "shape": ["number", { "decimalPlaces": 0 }] }, { "name": "completedPercentage", "displayName": "Completed %", "shape": ["number", { "decimalPlaces": 0 }] }, diff --git a/plugins/AutoTask/v1/dataStreams/tickets.json b/plugins/AutoTask/v1/dataStreams/tickets.json index 45edbad..e34fbdc 100644 --- a/plugins/AutoTask/v1/dataStreams/tickets.json +++ b/plugins/AutoTask/v1/dataStreams/tickets.json @@ -7,7 +7,7 @@ "httpMethod": "get", "expandInnerObjects": true, "endpointPath": "atservicesrest/v1.0/Tickets/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"gte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.start}}\"},{\"op\":\"lte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.end}}\"}]}", - "postRequestScript": "scripts/tickets.js", + "pathToData": "items", "paging": { "mode": "nextUrl", "pageSize": { "realm": "none" }, @@ -23,12 +23,14 @@ { "name": "statusName", "displayName": "Status", + "computed": true, + "valueExpression": "{{ $['status'] == 1 ? 'New' : $['status'] == 5 ? 'Complete' : $['status'] == 8 ? 'In Progress' : $['status'] == 9 ? 'Waiting Customer' : $['status'] == 10 ? 'Waiting Materials' : $['status'] == 11 ? 'Waiting Vendor' : $['status'] == 12 ? 'Escalate' : $['status'] == 13 ? 'Waiting Approval' : 'Unknown' }}", "shape": ["state", { "map": { "success": ["Complete"], "error": ["Escalate"], "warning": ["New", "In Progress"], - "unknown": ["Waiting Customer", "Waiting Materials", "Waiting Vendor", "Waiting Approval"] + "unknown": ["Waiting Customer", "Waiting Materials", "Waiting Vendor", "Waiting Approval", "Unknown"] } }] }, From eb642ce467e0f729d4539df2a9477455732c83c8 Mon Sep 17 00:00:00 2001 From: Tim Wheeler <63284593+TimWheeler-SQUP@users.noreply.github.com> Date: Thu, 30 Apr 2026 21:36:13 +0100 Subject: [PATCH 06/21] new data stream and removes .js ref --- .../AutoTask/v1/dataStreams/companies.json | 2 ++ plugins/AutoTask/v1/dataStreams/contacts.json | 10 ++++-- .../v1/dataStreams/contractStatus.json | 2 ++ .../v1/dataStreams/financialHealth.json | 2 ++ .../v1/dataStreams/projectStatus.json | 2 ++ .../v1/dataStreams/resourceUtilisation.json | 2 ++ .../v1/dataStreams/scripts/tickets.js | 15 --------- .../v1/dataStreams/surveyResults.json | 26 +++++++++++++++ plugins/AutoTask/v1/dataStreams/tickets.json | 2 ++ plugins/AutoTask/v1/docs/README.md | 32 +++++++++++++++++++ .../AutoTask/v1/indexDefinitions/default.json | 4 +-- 11 files changed, 80 insertions(+), 19 deletions(-) delete mode 100644 plugins/AutoTask/v1/dataStreams/scripts/tickets.js create mode 100644 plugins/AutoTask/v1/dataStreams/surveyResults.json create mode 100644 plugins/AutoTask/v1/docs/README.md diff --git a/plugins/AutoTask/v1/dataStreams/companies.json b/plugins/AutoTask/v1/dataStreams/companies.json index d493518..bd9f8ec 100644 --- a/plugins/AutoTask/v1/dataStreams/companies.json +++ b/plugins/AutoTask/v1/dataStreams/companies.json @@ -1,6 +1,8 @@ { "name": "companies", "displayName": "Companies", + "description": "All active companies in AutoTask", + "tags": ["companies"], "baseDataSourceName": "httpRequestUnscoped", "timeframes": false, "config": { diff --git a/plugins/AutoTask/v1/dataStreams/contacts.json b/plugins/AutoTask/v1/dataStreams/contacts.json index 97c73f2..466f2ea 100644 --- a/plugins/AutoTask/v1/dataStreams/contacts.json +++ b/plugins/AutoTask/v1/dataStreams/contacts.json @@ -18,8 +18,14 @@ } } }, - "metadata": [ - { "name": "id", "displayName": "ID", "shape":"string"}, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string" }, + { + "name": "fullName", + "displayName": "Full Name", + "computed": true, + "valueExpression": "{{ $['firstName'] + ' ' + $['lastName'] }}" + }, { "pattern": ".*" } ] } diff --git a/plugins/AutoTask/v1/dataStreams/contractStatus.json b/plugins/AutoTask/v1/dataStreams/contractStatus.json index 83e2c93..c77fb12 100644 --- a/plugins/AutoTask/v1/dataStreams/contractStatus.json +++ b/plugins/AutoTask/v1/dataStreams/contractStatus.json @@ -1,6 +1,8 @@ { "name": "contractStatus", "displayName": "Contract Status", + "description": "Status and key dates for all contracts", + "tags": ["contracts"], "baseDataSourceName": "httpRequestUnscoped", "timeframes": false, "config": { diff --git a/plugins/AutoTask/v1/dataStreams/financialHealth.json b/plugins/AutoTask/v1/dataStreams/financialHealth.json index cb18e6a..d8f8440 100644 --- a/plugins/AutoTask/v1/dataStreams/financialHealth.json +++ b/plugins/AutoTask/v1/dataStreams/financialHealth.json @@ -1,6 +1,8 @@ { "name": "financialHealth", "displayName": "Financial Health", + "description": "Estimated revenue, cost, and hours across all contracts", + "tags": ["finance"], "baseDataSourceName": "httpRequestUnscoped", "timeframes": false, "config": { diff --git a/plugins/AutoTask/v1/dataStreams/projectStatus.json b/plugins/AutoTask/v1/dataStreams/projectStatus.json index 9c726f8..ac64a8e 100644 --- a/plugins/AutoTask/v1/dataStreams/projectStatus.json +++ b/plugins/AutoTask/v1/dataStreams/projectStatus.json @@ -1,6 +1,8 @@ { "name": "projectStatus", "displayName": "Project Status", + "description": "Status, progress, and schedule for all projects", + "tags": ["projects"], "baseDataSourceName": "httpRequestUnscoped", "timeframes": false, "config": { diff --git a/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json b/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json index 85287a2..f69e73d 100644 --- a/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json +++ b/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json @@ -1,6 +1,8 @@ { "name": "resourceUtilisation", "displayName": "Resource Utilisation", + "description": "Time entries logged by resources within the selected timeframe", + "tags": ["resources"], "baseDataSourceName": "httpRequestUnscoped", "timeframes": ["last7days", "last30days", "thisMonth", "lastMonth", "thisQuarter", "lastQuarter", "thisYear"], "config": { diff --git a/plugins/AutoTask/v1/dataStreams/scripts/tickets.js b/plugins/AutoTask/v1/dataStreams/scripts/tickets.js deleted file mode 100644 index 831759b..0000000 --- a/plugins/AutoTask/v1/dataStreams/scripts/tickets.js +++ /dev/null @@ -1,15 +0,0 @@ -var statusMap = { - 1: { name: 'New', state: 'warning' }, - 5: { name: 'Complete', state: 'success' }, - 8: { name: 'In Progress', state: 'warning' }, - 9: { name: 'Waiting Customer', state: 'unknown' }, - 10: { name: 'Waiting Materials', state: 'unknown' }, - 11: { name: 'Waiting Vendor', state: 'unknown' }, - 12: { name: 'Escalate', state: 'error' }, - 13: { name: 'Waiting Approval', state: 'unknown' } -}; - -result = (data.items || []).map(function(ticket) { - var mapped = statusMap[ticket.status] || { name: 'Status ' + ticket.status, state: 'unknown' }; - return Object.assign({}, ticket, { statusName: mapped.name, stateValue: mapped.state }); -}); diff --git a/plugins/AutoTask/v1/dataStreams/surveyResults.json b/plugins/AutoTask/v1/dataStreams/surveyResults.json new file mode 100644 index 0000000..064fcad --- /dev/null +++ b/plugins/AutoTask/v1/dataStreams/surveyResults.json @@ -0,0 +1,26 @@ +{ + "name": "surveyResults", + "displayName": "Survey Results", + "description": "Survey results submitted within the selected timeframe", + "tags": ["surveys"], + "baseDataSourceName": "httpRequestUnscoped", + "timeframes": ["last24hours", "last7days", "last30days", "thisMonth", "lastMonth", "thisQuarter", "lastQuarter", "thisYear"], + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/SurveyResults/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"gte\",\"field\":\"createDate\",\"value\":\"{{timeframe.start}}\"},{\"op\":\"lte\",\"field\":\"createDate\",\"value\":\"{{timeframe.end}}\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": "none" }, + "in": { + "realm": "payload", + "path": "pageDetails.nextPageUrl" + } + } + }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, + { "pattern": ".*" } + ] +} diff --git a/plugins/AutoTask/v1/dataStreams/tickets.json b/plugins/AutoTask/v1/dataStreams/tickets.json index e34fbdc..8e2aaeb 100644 --- a/plugins/AutoTask/v1/dataStreams/tickets.json +++ b/plugins/AutoTask/v1/dataStreams/tickets.json @@ -1,6 +1,8 @@ { "name": "tickets", "displayName": "Tickets", + "description": "Tickets with last activity within the selected timeframe", + "tags": ["tickets"], "baseDataSourceName": "httpRequestUnscoped", "timeframes": ["last24hours", "last7days", "last30days", "thisMonth", "lastMonth", "thisQuarter", "lastQuarter", "thisYear"], "config": { diff --git a/plugins/AutoTask/v1/docs/README.md b/plugins/AutoTask/v1/docs/README.md new file mode 100644 index 0000000..0c2bb32 --- /dev/null +++ b/plugins/AutoTask/v1/docs/README.md @@ -0,0 +1,32 @@ +# Before you start + +You will need an AutoTask account with admin access to create an API User. API Users are a dedicated account type in AutoTask used for system integrations — they are separate from regular user accounts and do not consume a standard user licence. + +## Creating an API User + +1. Log in to AutoTask and go to **Admin > Resources/Users (HR) > Resources** +2. Click **New** and select **API User** as the resource type +3. Complete the required fields, including an email address — this becomes the **API Username** +4. Under the **Credentials** section, click **Generate** next to **Integration Code** to create your **API Integration Code** +5. Set a **Password / Secret** — this becomes the **API Secret** +6. Save the record + +The API User must have sufficient security permissions to read the entities you want to monitor (Companies, Tickets, Contracts, Projects, Resources, and Survey Results). + +## Finding your Zone URL + +Your Zone URL is based on the AutoTask data centre your account is hosted on. To find it: + +1. Log in to AutoTask and look at the URL in your browser address bar — for example `https://ww14.autotask.net` +2. Replace `ww` with `webservices` to get the API base URL — for example `https://webservices14.autotask.net` + +Enter the full base including `https://`. + +## Credential field reference + +| Field | Where to find it | +|---|---| +| **Zone URL** | Derived from your AutoTask login URL — see above | +| **API Integration Code** | Generated on the API User record under Credentials > Integration Code | +| **API Username** | The email address entered when creating the API User | +| **API Secret** | The password set on the API User record under Credentials | diff --git a/plugins/AutoTask/v1/indexDefinitions/default.json b/plugins/AutoTask/v1/indexDefinitions/default.json index 561cd47..e662e9f 100644 --- a/plugins/AutoTask/v1/indexDefinitions/default.json +++ b/plugins/AutoTask/v1/indexDefinitions/default.json @@ -22,7 +22,7 @@ "timeframe": "none", "objectMapping": { "id": "id", - "name": "firstName", + "name": "fullName", "type": { "value": "autotask-contact" }, "properties": [ "lastName", @@ -55,7 +55,7 @@ "timeframe": "none", "objectMapping": { "id": "id", - "name": "firstName", + "name": "email", "type": { "value": "autotask-resource" }, "properties": [ "lastName", From 15f856f53ef6f8dab7d0d89e25ee1b0410ac7f35 Mon Sep 17 00:00:00 2001 From: Tim Wheeler <63284593+TimWheeler-SQUP@users.noreply.github.com> Date: Sat, 25 Apr 2026 20:11:50 +0100 Subject: [PATCH 07/21] New AutoTask plugin and data streams --- plugins/AutoTask/configValidation.json | 11 +++ plugins/AutoTask/custom_types.json | 37 ++++++++ plugins/AutoTask/dataStreams/companies.json | 21 +++++ plugins/AutoTask/dataStreams/contacts.json | 21 +++++ .../AutoTask/dataStreams/contractStatus.json | 30 +++++++ plugins/AutoTask/dataStreams/contracts.json | 21 +++++ .../AutoTask/dataStreams/financialHealth.json | 30 +++++++ .../AutoTask/dataStreams/projectStatus.json | 31 +++++++ plugins/AutoTask/dataStreams/projects.json | 21 +++++ .../dataStreams/resourceUtilisation.json | 30 +++++++ plugins/AutoTask/dataStreams/resources.json | 21 +++++ .../AutoTask/dataStreams/scripts/tickets.js | 15 ++++ plugins/AutoTask/dataStreams/tickets.json | 47 ++++++++++ plugins/AutoTask/icon.png | Bin 0 -> 13387 bytes .../AutoTask/indexDefinitions/default.json | 85 ++++++++++++++++++ plugins/AutoTask/metadata.json | 48 ++++++++++ plugins/AutoTask/ui.json | 40 +++++++++ 17 files changed, 509 insertions(+) create mode 100644 plugins/AutoTask/configValidation.json create mode 100644 plugins/AutoTask/custom_types.json create mode 100644 plugins/AutoTask/dataStreams/companies.json create mode 100644 plugins/AutoTask/dataStreams/contacts.json create mode 100644 plugins/AutoTask/dataStreams/contractStatus.json create mode 100644 plugins/AutoTask/dataStreams/contracts.json create mode 100644 plugins/AutoTask/dataStreams/financialHealth.json create mode 100644 plugins/AutoTask/dataStreams/projectStatus.json create mode 100644 plugins/AutoTask/dataStreams/projects.json create mode 100644 plugins/AutoTask/dataStreams/resourceUtilisation.json create mode 100644 plugins/AutoTask/dataStreams/resources.json create mode 100644 plugins/AutoTask/dataStreams/scripts/tickets.js create mode 100644 plugins/AutoTask/dataStreams/tickets.json create mode 100644 plugins/AutoTask/icon.png create mode 100644 plugins/AutoTask/indexDefinitions/default.json create mode 100644 plugins/AutoTask/metadata.json create mode 100644 plugins/AutoTask/ui.json diff --git a/plugins/AutoTask/configValidation.json b/plugins/AutoTask/configValidation.json new file mode 100644 index 0000000..c250fab --- /dev/null +++ b/plugins/AutoTask/configValidation.json @@ -0,0 +1,11 @@ +{ + "steps": [ + { + "displayName": "AutoTask connection", + "dataStream": { "name": "companies" }, + "success": "Successfully connected to AutoTask", + "error": "Cannot connect to AutoTask — check your Zone URL, Integration Code, Username and Secret", + "required": true + } + ] +} diff --git a/plugins/AutoTask/custom_types.json b/plugins/AutoTask/custom_types.json new file mode 100644 index 0000000..24c6635 --- /dev/null +++ b/plugins/AutoTask/custom_types.json @@ -0,0 +1,37 @@ +[ + { + "name": "AutoTask Company", + "sourceType": "autotask-company", + "icon": "building-2", + "singular": "Company", + "plural": "Companies" + }, + { + "name": "AutoTask Contact", + "sourceType": "autotask-contact", + "icon": "user", + "singular": "Contact", + "plural": "Contacts" + }, + { + "name": "AutoTask Project", + "sourceType": "autotask-project", + "icon": "folder-kanban", + "singular": "Project", + "plural": "Projects" + }, + { + "name": "AutoTask Resource", + "sourceType": "autotask-resource", + "icon": "hard-hat", + "singular": "Resource", + "plural": "Resources" + }, + { + "name": "AutoTask Contract", + "sourceType": "autotask-contract", + "icon": "file-text", + "singular": "Contract", + "plural": "Contracts" + } +] diff --git a/plugins/AutoTask/dataStreams/companies.json b/plugins/AutoTask/dataStreams/companies.json new file mode 100644 index 0000000..dba1606 --- /dev/null +++ b/plugins/AutoTask/dataStreams/companies.json @@ -0,0 +1,21 @@ +{ + "name": "companies", + "displayName": "Companies", + "baseDataSourceName": "httpRequestUnscoped", + "visibility": { "type": "hidden" }, + "timeframes": false, + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/Companies/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"exist\",\"field\":\"id\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + } +} diff --git a/plugins/AutoTask/dataStreams/contacts.json b/plugins/AutoTask/dataStreams/contacts.json new file mode 100644 index 0000000..d01971a --- /dev/null +++ b/plugins/AutoTask/dataStreams/contacts.json @@ -0,0 +1,21 @@ +{ + "name": "contacts", + "displayName": "Contacts", + "baseDataSourceName": "httpRequestUnscoped", + "visibility": { "type": "hidden" }, + "timeframes": false, + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/Contacts/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"exist\",\"field\":\"id\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + } +} diff --git a/plugins/AutoTask/dataStreams/contractStatus.json b/plugins/AutoTask/dataStreams/contractStatus.json new file mode 100644 index 0000000..15bc11a --- /dev/null +++ b/plugins/AutoTask/dataStreams/contractStatus.json @@ -0,0 +1,30 @@ +{ + "name": "contractStatus", + "displayName": "Contract Status", + "baseDataSourceName": "httpRequestUnscoped", + "timeframes": false, + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/Contracts/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"exist\",\"field\":\"id\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + }, + "metadata": [ + { "name": "id", "displayName": "ID", "visible": false }, + { "name": "contractName", "displayName": "Contract Name" }, + { "name": "contractType", "displayName": "Contract Type", "shape": ["number", { "decimalPlaces": 0 }] }, + { "name": "status", "displayName": "Status", "shape": ["number", { "decimalPlaces": 0 }] }, + { "name": "startDate", "displayName": "Start Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "endDate", "displayName": "End Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "companyID", "displayName": "Company ID" }, + { "pattern": ".*" } + ] +} diff --git a/plugins/AutoTask/dataStreams/contracts.json b/plugins/AutoTask/dataStreams/contracts.json new file mode 100644 index 0000000..76937bd --- /dev/null +++ b/plugins/AutoTask/dataStreams/contracts.json @@ -0,0 +1,21 @@ +{ + "name": "contracts", + "displayName": "Contracts", + "baseDataSourceName": "httpRequestUnscoped", + "visibility": { "type": "hidden" }, + "timeframes": false, + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/Contracts/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"exist\",\"field\":\"id\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + } +} diff --git a/plugins/AutoTask/dataStreams/financialHealth.json b/plugins/AutoTask/dataStreams/financialHealth.json new file mode 100644 index 0000000..caa3707 --- /dev/null +++ b/plugins/AutoTask/dataStreams/financialHealth.json @@ -0,0 +1,30 @@ +{ + "name": "financialHealth", + "displayName": "Financial Health", + "baseDataSourceName": "httpRequestUnscoped", + "timeframes": false, + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/Contracts/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"exist\",\"field\":\"id\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + }, + "metadata": [ + { "name": "id", "displayName": "ID", "visible": false }, + { "name": "contractName", "displayName": "Contract Name" }, + { "name": "contractType", "displayName": "Contract Type", "shape": ["number", { "decimalPlaces": 0 }] }, + { "name": "estimatedRevenue", "displayName": "Estimated Revenue", "shape": ["number", { "decimalPlaces": 2 }] }, + { "name": "estimatedCost", "displayName": "Estimated Cost", "shape": ["number", { "decimalPlaces": 2 }] }, + { "name": "estimatedHours", "displayName": "Estimated Hours", "shape": ["number", { "decimalPlaces": 2 }] }, + { "name": "status", "displayName": "Status", "shape": ["number", { "decimalPlaces": 0 }] }, + { "pattern": ".*" } + ] +} diff --git a/plugins/AutoTask/dataStreams/projectStatus.json b/plugins/AutoTask/dataStreams/projectStatus.json new file mode 100644 index 0000000..6a1e5cd --- /dev/null +++ b/plugins/AutoTask/dataStreams/projectStatus.json @@ -0,0 +1,31 @@ +{ + "name": "projectStatus", + "displayName": "Project Status", + "baseDataSourceName": "httpRequestUnscoped", + "timeframes": false, + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/Projects/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"exist\",\"field\":\"id\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + }, + "metadata": [ + { "name": "id", "displayName": "ID", "visible": false }, + { "name": "projectName", "displayName": "Project Name" }, + { "name": "status", "displayName": "Status", "shape": ["number", { "decimalPlaces": 0 }] }, + { "name": "completedPercentage", "displayName": "Completed %", "shape": ["number", { "decimalPlaces": 0 }] }, + { "name": "actualHours", "displayName": "Actual Hours", "shape": ["number", { "decimalPlaces": 2 }] }, + { "name": "estimatedTime", "displayName": "Estimated Hours", "shape": ["number", { "decimalPlaces": 2 }] }, + { "name": "startDateTime", "displayName": "Start Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "endDateTime", "displayName": "End Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "pattern": ".*" } + ] +} diff --git a/plugins/AutoTask/dataStreams/projects.json b/plugins/AutoTask/dataStreams/projects.json new file mode 100644 index 0000000..3d5d322 --- /dev/null +++ b/plugins/AutoTask/dataStreams/projects.json @@ -0,0 +1,21 @@ +{ + "name": "projects", + "displayName": "Projects", + "baseDataSourceName": "httpRequestUnscoped", + "visibility": { "type": "hidden" }, + "timeframes": false, + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/Projects/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"exist\",\"field\":\"id\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + } +} diff --git a/plugins/AutoTask/dataStreams/resourceUtilisation.json b/plugins/AutoTask/dataStreams/resourceUtilisation.json new file mode 100644 index 0000000..a38f9c7 --- /dev/null +++ b/plugins/AutoTask/dataStreams/resourceUtilisation.json @@ -0,0 +1,30 @@ +{ + "name": "resourceUtilisation", + "displayName": "Resource Utilisation", + "baseDataSourceName": "httpRequestUnscoped", + "timeframes": ["last7days", "last30days", "thisMonth", "lastMonth", "thisQuarter", "lastQuarter", "thisYear"], + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/TimeEntries/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"gte\",\"field\":\"dateWorked\",\"value\":\"{{timeframe.start}}\"},{\"op\":\"lte\",\"field\":\"dateWorked\",\"value\":\"{{timeframe.end}}\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + }, + "metadata": [ + { "name": "id", "displayName": "ID", "visible": false }, + { "name": "resourceID", "displayName": "Resource ID" }, + { "name": "dateWorked", "displayName": "Date Worked", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "hoursWorked", "displayName": "Hours Worked", "shape": ["number", { "decimalPlaces": 2 }] }, + { "name": "hoursToBill", "displayName": "Hours to Bill", "shape": ["number", { "decimalPlaces": 2 }] }, + { "name": "billingCodeID", "displayName": "Billing Code ID" }, + { "name": "ticketID", "displayName": "Ticket ID" }, + { "pattern": ".*" } + ] +} diff --git a/plugins/AutoTask/dataStreams/resources.json b/plugins/AutoTask/dataStreams/resources.json new file mode 100644 index 0000000..72dc1c2 --- /dev/null +++ b/plugins/AutoTask/dataStreams/resources.json @@ -0,0 +1,21 @@ +{ + "name": "resources", + "displayName": "Resources", + "baseDataSourceName": "httpRequestUnscoped", + "visibility": { "type": "hidden" }, + "timeframes": false, + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/Resources/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"exist\",\"field\":\"id\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + } +} diff --git a/plugins/AutoTask/dataStreams/scripts/tickets.js b/plugins/AutoTask/dataStreams/scripts/tickets.js new file mode 100644 index 0000000..831759b --- /dev/null +++ b/plugins/AutoTask/dataStreams/scripts/tickets.js @@ -0,0 +1,15 @@ +var statusMap = { + 1: { name: 'New', state: 'warning' }, + 5: { name: 'Complete', state: 'success' }, + 8: { name: 'In Progress', state: 'warning' }, + 9: { name: 'Waiting Customer', state: 'unknown' }, + 10: { name: 'Waiting Materials', state: 'unknown' }, + 11: { name: 'Waiting Vendor', state: 'unknown' }, + 12: { name: 'Escalate', state: 'error' }, + 13: { name: 'Waiting Approval', state: 'unknown' } +}; + +result = (data.items || []).map(function(ticket) { + var mapped = statusMap[ticket.status] || { name: 'Status ' + ticket.status, state: 'unknown' }; + return Object.assign({}, ticket, { statusName: mapped.name, stateValue: mapped.state }); +}); diff --git a/plugins/AutoTask/dataStreams/tickets.json b/plugins/AutoTask/dataStreams/tickets.json new file mode 100644 index 0000000..872f54e --- /dev/null +++ b/plugins/AutoTask/dataStreams/tickets.json @@ -0,0 +1,47 @@ +{ + "name": "tickets", + "displayName": "Tickets", + "baseDataSourceName": "httpRequestUnscoped", + "timeframes": ["last24hours", "last7days", "last30days", "thisMonth", "lastMonth", "thisQuarter", "lastQuarter", "thisYear"], + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/Tickets/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"gte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.start}}\"},{\"op\":\"lte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.end}}\"}]}", + "pathToData": "", + "postRequestScript": "scripts/tickets.js", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } + } + }, + "metadata": [ + { "name": "id", "displayName": "Ticket ID", "shape": "string" }, + { "name": "title", "displayName": "Title" }, + { + "name": "statusName", + "displayName": "Status", + "shape": ["state", { + "map": { + "success": ["Complete"], + "error": ["Escalate"], + "warning": ["New", "In Progress"], + "unknown": ["Waiting Customer", "Waiting Materials", "Waiting Vendor", "Waiting Approval"] + } + }] + }, + { "name": "priority", "displayName": "Priority", "shape": ["number", { "decimalPlaces": 0 }] }, + { "name": "companyID", "sourceType": "autotask-company", "visible": false }, + { "name": "companyName", "displayName": "Company", "sourceId": "companyID", "objectPropertyPath": "name" }, + { "name": "contactID", "sourceType": "autotask-contact", "visible": false }, + { "name": "contactName", "displayName": "Contact", "sourceId": "contactID", "objectPropertyPath": "name" }, + { "name": "assignedResourceID", "sourceType": "autotask-resource", "visible": false }, + { "name": "assignedResourceName", "displayName": "Assigned Resource", "sourceId": "assignedResourceID", "objectPropertyPath": "name" }, + { "name": "lastActivityDate", "displayName": "Last Activity Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "dueDateTime", "displayName": "Due Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "pattern": ".*" } + ] +} diff --git a/plugins/AutoTask/icon.png b/plugins/AutoTask/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ccee9663d65d9ef8ecb6b3fde0b324a531f68572 GIT binary patch literal 13387 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 Resources > API User", + "validation": { + "required": true + } + }, + { + "type": "text", + "name": "userName", + "label": "API Username", + "placeholder": "apiuser@yourdomain.com", + "help": "The email address of your AutoTask API User", + "validation": { + "required": true + } + }, + { + "type": "password", + "name": "secret", + "label": "API Secret", + "help": "Generated when creating an API User in AutoTask", + "validation": { + "required": true + } + } +] From 972270997ca8c8c262d04f9db8885500f091a1c7 Mon Sep 17 00:00:00 2001 From: Tim Wheeler <63284593+TimWheeler-SQUP@users.noreply.github.com> Date: Mon, 27 Apr 2026 12:09:00 +0100 Subject: [PATCH 08/21] Update folder --- plugins/AutoTask/{ => v1}/configValidation.json | 0 plugins/AutoTask/{ => v1}/custom_types.json | 0 .../AutoTask/{ => v1}/dataStreams/companies.json | 0 plugins/AutoTask/{ => v1}/dataStreams/contacts.json | 0 .../{ => v1}/dataStreams/contractStatus.json | 0 .../AutoTask/{ => v1}/dataStreams/contracts.json | 0 .../{ => v1}/dataStreams/financialHealth.json | 0 .../{ => v1}/dataStreams/projectStatus.json | 0 plugins/AutoTask/{ => v1}/dataStreams/projects.json | 0 .../{ => v1}/dataStreams/resourceUtilisation.json | 0 .../AutoTask/{ => v1}/dataStreams/resources.json | 0 .../{ => v1}/dataStreams/scripts/tickets.js | 0 plugins/AutoTask/{ => v1}/dataStreams/tickets.json | 0 plugins/AutoTask/{ => v1}/icon.png | Bin .../AutoTask/{ => v1}/indexDefinitions/default.json | 0 plugins/AutoTask/{ => v1}/metadata.json | 0 plugins/AutoTask/{ => v1}/ui.json | 0 17 files changed, 0 insertions(+), 0 deletions(-) rename plugins/AutoTask/{ => v1}/configValidation.json (100%) rename plugins/AutoTask/{ => v1}/custom_types.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/companies.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/contacts.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/contractStatus.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/contracts.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/financialHealth.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/projectStatus.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/projects.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/resourceUtilisation.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/resources.json (100%) rename plugins/AutoTask/{ => v1}/dataStreams/scripts/tickets.js (100%) rename plugins/AutoTask/{ => v1}/dataStreams/tickets.json (100%) rename plugins/AutoTask/{ => v1}/icon.png (100%) rename plugins/AutoTask/{ => v1}/indexDefinitions/default.json (100%) rename plugins/AutoTask/{ => v1}/metadata.json (100%) rename plugins/AutoTask/{ => v1}/ui.json (100%) diff --git a/plugins/AutoTask/configValidation.json b/plugins/AutoTask/v1/configValidation.json similarity index 100% rename from plugins/AutoTask/configValidation.json rename to plugins/AutoTask/v1/configValidation.json diff --git a/plugins/AutoTask/custom_types.json b/plugins/AutoTask/v1/custom_types.json similarity index 100% rename from plugins/AutoTask/custom_types.json rename to plugins/AutoTask/v1/custom_types.json diff --git a/plugins/AutoTask/dataStreams/companies.json b/plugins/AutoTask/v1/dataStreams/companies.json similarity index 100% rename from plugins/AutoTask/dataStreams/companies.json rename to plugins/AutoTask/v1/dataStreams/companies.json diff --git a/plugins/AutoTask/dataStreams/contacts.json b/plugins/AutoTask/v1/dataStreams/contacts.json similarity index 100% rename from plugins/AutoTask/dataStreams/contacts.json rename to plugins/AutoTask/v1/dataStreams/contacts.json diff --git a/plugins/AutoTask/dataStreams/contractStatus.json b/plugins/AutoTask/v1/dataStreams/contractStatus.json similarity index 100% rename from plugins/AutoTask/dataStreams/contractStatus.json rename to plugins/AutoTask/v1/dataStreams/contractStatus.json diff --git a/plugins/AutoTask/dataStreams/contracts.json b/plugins/AutoTask/v1/dataStreams/contracts.json similarity index 100% rename from plugins/AutoTask/dataStreams/contracts.json rename to plugins/AutoTask/v1/dataStreams/contracts.json diff --git a/plugins/AutoTask/dataStreams/financialHealth.json b/plugins/AutoTask/v1/dataStreams/financialHealth.json similarity index 100% rename from plugins/AutoTask/dataStreams/financialHealth.json rename to plugins/AutoTask/v1/dataStreams/financialHealth.json diff --git a/plugins/AutoTask/dataStreams/projectStatus.json b/plugins/AutoTask/v1/dataStreams/projectStatus.json similarity index 100% rename from plugins/AutoTask/dataStreams/projectStatus.json rename to plugins/AutoTask/v1/dataStreams/projectStatus.json diff --git a/plugins/AutoTask/dataStreams/projects.json b/plugins/AutoTask/v1/dataStreams/projects.json similarity index 100% rename from plugins/AutoTask/dataStreams/projects.json rename to plugins/AutoTask/v1/dataStreams/projects.json diff --git a/plugins/AutoTask/dataStreams/resourceUtilisation.json b/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json similarity index 100% rename from plugins/AutoTask/dataStreams/resourceUtilisation.json rename to plugins/AutoTask/v1/dataStreams/resourceUtilisation.json diff --git a/plugins/AutoTask/dataStreams/resources.json b/plugins/AutoTask/v1/dataStreams/resources.json similarity index 100% rename from plugins/AutoTask/dataStreams/resources.json rename to plugins/AutoTask/v1/dataStreams/resources.json diff --git a/plugins/AutoTask/dataStreams/scripts/tickets.js b/plugins/AutoTask/v1/dataStreams/scripts/tickets.js similarity index 100% rename from plugins/AutoTask/dataStreams/scripts/tickets.js rename to plugins/AutoTask/v1/dataStreams/scripts/tickets.js diff --git a/plugins/AutoTask/dataStreams/tickets.json b/plugins/AutoTask/v1/dataStreams/tickets.json similarity index 100% rename from plugins/AutoTask/dataStreams/tickets.json rename to plugins/AutoTask/v1/dataStreams/tickets.json diff --git a/plugins/AutoTask/icon.png b/plugins/AutoTask/v1/icon.png similarity index 100% rename from plugins/AutoTask/icon.png rename to plugins/AutoTask/v1/icon.png diff --git a/plugins/AutoTask/indexDefinitions/default.json b/plugins/AutoTask/v1/indexDefinitions/default.json similarity index 100% rename from plugins/AutoTask/indexDefinitions/default.json rename to plugins/AutoTask/v1/indexDefinitions/default.json diff --git a/plugins/AutoTask/metadata.json b/plugins/AutoTask/v1/metadata.json similarity index 100% rename from plugins/AutoTask/metadata.json rename to plugins/AutoTask/v1/metadata.json diff --git a/plugins/AutoTask/ui.json b/plugins/AutoTask/v1/ui.json similarity index 100% rename from plugins/AutoTask/ui.json rename to plugins/AutoTask/v1/ui.json From a9f1a7b8c0b1cfe44442893ad499456740c78faa Mon Sep 17 00:00:00 2001 From: Tim Wheeler <63284593+TimWheeler-SQUP@users.noreply.github.com> Date: Tue, 28 Apr 2026 07:47:12 +0100 Subject: [PATCH 09/21] Fix paging --- plugins/AutoTask/v1/configValidation.json | 2 +- plugins/AutoTask/v1/dataStreams/companies.json | 5 ++--- plugins/AutoTask/v1/dataStreams/contacts.json | 4 ++-- plugins/AutoTask/v1/dataStreams/contractStatus.json | 4 ++-- plugins/AutoTask/v1/dataStreams/contracts.json | 4 ++-- plugins/AutoTask/v1/dataStreams/financialHealth.json | 4 ++-- plugins/AutoTask/v1/dataStreams/projectStatus.json | 4 ++-- plugins/AutoTask/v1/dataStreams/projects.json | 4 ++-- plugins/AutoTask/v1/dataStreams/resourceUtilisation.json | 4 ++-- plugins/AutoTask/v1/dataStreams/resources.json | 4 ++-- plugins/AutoTask/v1/dataStreams/tickets.json | 5 ++--- 11 files changed, 21 insertions(+), 23 deletions(-) diff --git a/plugins/AutoTask/v1/configValidation.json b/plugins/AutoTask/v1/configValidation.json index c250fab..3212a51 100644 --- a/plugins/AutoTask/v1/configValidation.json +++ b/plugins/AutoTask/v1/configValidation.json @@ -2,7 +2,7 @@ "steps": [ { "displayName": "AutoTask connection", - "dataStream": { "name": "companies" }, + "dataStream": { "name": "companies", "timeframe": "none" }, "success": "Successfully connected to AutoTask", "error": "Cannot connect to AutoTask — check your Zone URL, Integration Code, Username and Secret", "required": true diff --git a/plugins/AutoTask/v1/dataStreams/companies.json b/plugins/AutoTask/v1/dataStreams/companies.json index dba1606..d493518 100644 --- a/plugins/AutoTask/v1/dataStreams/companies.json +++ b/plugins/AutoTask/v1/dataStreams/companies.json @@ -2,7 +2,6 @@ "name": "companies", "displayName": "Companies", "baseDataSourceName": "httpRequestUnscoped", - "visibility": { "type": "hidden" }, "timeframes": false, "config": { "httpMethod": "get", @@ -11,9 +10,9 @@ "pathToData": "items", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } diff --git a/plugins/AutoTask/v1/dataStreams/contacts.json b/plugins/AutoTask/v1/dataStreams/contacts.json index d01971a..5bd1e41 100644 --- a/plugins/AutoTask/v1/dataStreams/contacts.json +++ b/plugins/AutoTask/v1/dataStreams/contacts.json @@ -11,9 +11,9 @@ "pathToData": "items", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } diff --git a/plugins/AutoTask/v1/dataStreams/contractStatus.json b/plugins/AutoTask/v1/dataStreams/contractStatus.json index 15bc11a..83e2c93 100644 --- a/plugins/AutoTask/v1/dataStreams/contractStatus.json +++ b/plugins/AutoTask/v1/dataStreams/contractStatus.json @@ -10,9 +10,9 @@ "pathToData": "items", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } diff --git a/plugins/AutoTask/v1/dataStreams/contracts.json b/plugins/AutoTask/v1/dataStreams/contracts.json index 76937bd..fe9ad48 100644 --- a/plugins/AutoTask/v1/dataStreams/contracts.json +++ b/plugins/AutoTask/v1/dataStreams/contracts.json @@ -11,9 +11,9 @@ "pathToData": "items", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } diff --git a/plugins/AutoTask/v1/dataStreams/financialHealth.json b/plugins/AutoTask/v1/dataStreams/financialHealth.json index caa3707..cb18e6a 100644 --- a/plugins/AutoTask/v1/dataStreams/financialHealth.json +++ b/plugins/AutoTask/v1/dataStreams/financialHealth.json @@ -10,9 +10,9 @@ "pathToData": "items", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } diff --git a/plugins/AutoTask/v1/dataStreams/projectStatus.json b/plugins/AutoTask/v1/dataStreams/projectStatus.json index 6a1e5cd..bebb1a2 100644 --- a/plugins/AutoTask/v1/dataStreams/projectStatus.json +++ b/plugins/AutoTask/v1/dataStreams/projectStatus.json @@ -10,9 +10,9 @@ "pathToData": "items", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } diff --git a/plugins/AutoTask/v1/dataStreams/projects.json b/plugins/AutoTask/v1/dataStreams/projects.json index 3d5d322..cfe895f 100644 --- a/plugins/AutoTask/v1/dataStreams/projects.json +++ b/plugins/AutoTask/v1/dataStreams/projects.json @@ -11,9 +11,9 @@ "pathToData": "items", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } diff --git a/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json b/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json index a38f9c7..85287a2 100644 --- a/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json +++ b/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json @@ -10,9 +10,9 @@ "pathToData": "items", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } diff --git a/plugins/AutoTask/v1/dataStreams/resources.json b/plugins/AutoTask/v1/dataStreams/resources.json index 72dc1c2..f0321e9 100644 --- a/plugins/AutoTask/v1/dataStreams/resources.json +++ b/plugins/AutoTask/v1/dataStreams/resources.json @@ -11,9 +11,9 @@ "pathToData": "items", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } diff --git a/plugins/AutoTask/v1/dataStreams/tickets.json b/plugins/AutoTask/v1/dataStreams/tickets.json index 872f54e..45edbad 100644 --- a/plugins/AutoTask/v1/dataStreams/tickets.json +++ b/plugins/AutoTask/v1/dataStreams/tickets.json @@ -7,13 +7,12 @@ "httpMethod": "get", "expandInnerObjects": true, "endpointPath": "atservicesrest/v1.0/Tickets/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"gte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.start}}\"},{\"op\":\"lte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.end}}\"}]}", - "pathToData": "", "postRequestScript": "scripts/tickets.js", "paging": { "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, + "pageSize": { "realm": "none" }, "in": { - "realm": { "value": "payload", "label": "payload" }, + "realm": "payload", "path": "pageDetails.nextPageUrl" } } From 7f86a27e4c638783ad97d2cae3e220c57dbf3f26 Mon Sep 17 00:00:00 2001 From: Tim Wheeler <63284593+TimWheeler-SQUP@users.noreply.github.com> Date: Wed, 29 Apr 2026 17:54:13 +0100 Subject: [PATCH 10/21] update icon --- plugins/AutoTask/v1/icon.png | Bin 13387 -> 7405 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/plugins/AutoTask/v1/icon.png b/plugins/AutoTask/v1/icon.png index ccee9663d65d9ef8ecb6b3fde0b324a531f68572..c64f4383e75ab8062c973f1383136bb7c0c75abc 100644 GIT binary patch literal 7405 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 Date: Thu, 30 Apr 2026 09:14:57 +0100 Subject: [PATCH 11/21] remove js file and updated status field --- CLAUDE.md | 23 +++++++++++++++++++ plugins/AutoTask/v1/dataStreams/contacts.json | 6 ++++- .../v1/dataStreams/projectStatus.json | 2 +- plugins/AutoTask/v1/dataStreams/tickets.json | 6 +++-- 4 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8ff872c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,23 @@ +# Plugin Authoring Guidelines + +## Status mappings + +Where an API returns a numeric or coded status value that needs mapping to a human-readable label and a SquaredUp state, implement the mapping directly in the data stream `.json` file using a `computed` column with a `valueExpression` ternary chain. Only use a `.js` post-request script when the mapping logic is too complex for an expression (e.g. requires lookups across multiple fields or external data). + +Example: + +```json +{ + "name": "statusName", + "displayName": "Status", + "computed": true, + "valueExpression": "{{ $['status'] == 1 ? 'Open' : $['status'] == 2 ? 'Closed' : 'Unknown' }}", + "shape": ["state", { + "map": { + "warning": ["Open"], + "success": ["Closed"], + "unknown": ["Unknown"] + } + }] +} +``` diff --git a/plugins/AutoTask/v1/dataStreams/contacts.json b/plugins/AutoTask/v1/dataStreams/contacts.json index 5bd1e41..97c73f2 100644 --- a/plugins/AutoTask/v1/dataStreams/contacts.json +++ b/plugins/AutoTask/v1/dataStreams/contacts.json @@ -17,5 +17,9 @@ "path": "pageDetails.nextPageUrl" } } - } + }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape":"string"}, + { "pattern": ".*" } + ] } diff --git a/plugins/AutoTask/v1/dataStreams/projectStatus.json b/plugins/AutoTask/v1/dataStreams/projectStatus.json index bebb1a2..9c726f8 100644 --- a/plugins/AutoTask/v1/dataStreams/projectStatus.json +++ b/plugins/AutoTask/v1/dataStreams/projectStatus.json @@ -18,7 +18,7 @@ } }, "metadata": [ - { "name": "id", "displayName": "ID", "visible": false }, + { "name": "id", "displayName": "ID", "shape": "string" }, { "name": "projectName", "displayName": "Project Name" }, { "name": "status", "displayName": "Status", "shape": ["number", { "decimalPlaces": 0 }] }, { "name": "completedPercentage", "displayName": "Completed %", "shape": ["number", { "decimalPlaces": 0 }] }, diff --git a/plugins/AutoTask/v1/dataStreams/tickets.json b/plugins/AutoTask/v1/dataStreams/tickets.json index 45edbad..e34fbdc 100644 --- a/plugins/AutoTask/v1/dataStreams/tickets.json +++ b/plugins/AutoTask/v1/dataStreams/tickets.json @@ -7,7 +7,7 @@ "httpMethod": "get", "expandInnerObjects": true, "endpointPath": "atservicesrest/v1.0/Tickets/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"gte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.start}}\"},{\"op\":\"lte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.end}}\"}]}", - "postRequestScript": "scripts/tickets.js", + "pathToData": "items", "paging": { "mode": "nextUrl", "pageSize": { "realm": "none" }, @@ -23,12 +23,14 @@ { "name": "statusName", "displayName": "Status", + "computed": true, + "valueExpression": "{{ $['status'] == 1 ? 'New' : $['status'] == 5 ? 'Complete' : $['status'] == 8 ? 'In Progress' : $['status'] == 9 ? 'Waiting Customer' : $['status'] == 10 ? 'Waiting Materials' : $['status'] == 11 ? 'Waiting Vendor' : $['status'] == 12 ? 'Escalate' : $['status'] == 13 ? 'Waiting Approval' : 'Unknown' }}", "shape": ["state", { "map": { "success": ["Complete"], "error": ["Escalate"], "warning": ["New", "In Progress"], - "unknown": ["Waiting Customer", "Waiting Materials", "Waiting Vendor", "Waiting Approval"] + "unknown": ["Waiting Customer", "Waiting Materials", "Waiting Vendor", "Waiting Approval", "Unknown"] } }] }, From cae7a4459dfc94ae8bba297c5ae46141d053d0ac Mon Sep 17 00:00:00 2001 From: Tim Wheeler <63284593+TimWheeler-SQUP@users.noreply.github.com> Date: Thu, 30 Apr 2026 21:36:13 +0100 Subject: [PATCH 12/21] new data stream and removes .js ref --- .../AutoTask/v1/dataStreams/companies.json | 2 ++ plugins/AutoTask/v1/dataStreams/contacts.json | 10 ++++-- .../v1/dataStreams/contractStatus.json | 2 ++ .../v1/dataStreams/financialHealth.json | 2 ++ .../v1/dataStreams/projectStatus.json | 2 ++ .../v1/dataStreams/resourceUtilisation.json | 2 ++ .../v1/dataStreams/scripts/tickets.js | 15 --------- .../v1/dataStreams/surveyResults.json | 26 +++++++++++++++ plugins/AutoTask/v1/dataStreams/tickets.json | 2 ++ plugins/AutoTask/v1/docs/README.md | 32 +++++++++++++++++++ .../AutoTask/v1/indexDefinitions/default.json | 4 +-- 11 files changed, 80 insertions(+), 19 deletions(-) delete mode 100644 plugins/AutoTask/v1/dataStreams/scripts/tickets.js create mode 100644 plugins/AutoTask/v1/dataStreams/surveyResults.json create mode 100644 plugins/AutoTask/v1/docs/README.md diff --git a/plugins/AutoTask/v1/dataStreams/companies.json b/plugins/AutoTask/v1/dataStreams/companies.json index d493518..bd9f8ec 100644 --- a/plugins/AutoTask/v1/dataStreams/companies.json +++ b/plugins/AutoTask/v1/dataStreams/companies.json @@ -1,6 +1,8 @@ { "name": "companies", "displayName": "Companies", + "description": "All active companies in AutoTask", + "tags": ["companies"], "baseDataSourceName": "httpRequestUnscoped", "timeframes": false, "config": { diff --git a/plugins/AutoTask/v1/dataStreams/contacts.json b/plugins/AutoTask/v1/dataStreams/contacts.json index 97c73f2..466f2ea 100644 --- a/plugins/AutoTask/v1/dataStreams/contacts.json +++ b/plugins/AutoTask/v1/dataStreams/contacts.json @@ -18,8 +18,14 @@ } } }, - "metadata": [ - { "name": "id", "displayName": "ID", "shape":"string"}, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string" }, + { + "name": "fullName", + "displayName": "Full Name", + "computed": true, + "valueExpression": "{{ $['firstName'] + ' ' + $['lastName'] }}" + }, { "pattern": ".*" } ] } diff --git a/plugins/AutoTask/v1/dataStreams/contractStatus.json b/plugins/AutoTask/v1/dataStreams/contractStatus.json index 83e2c93..c77fb12 100644 --- a/plugins/AutoTask/v1/dataStreams/contractStatus.json +++ b/plugins/AutoTask/v1/dataStreams/contractStatus.json @@ -1,6 +1,8 @@ { "name": "contractStatus", "displayName": "Contract Status", + "description": "Status and key dates for all contracts", + "tags": ["contracts"], "baseDataSourceName": "httpRequestUnscoped", "timeframes": false, "config": { diff --git a/plugins/AutoTask/v1/dataStreams/financialHealth.json b/plugins/AutoTask/v1/dataStreams/financialHealth.json index cb18e6a..d8f8440 100644 --- a/plugins/AutoTask/v1/dataStreams/financialHealth.json +++ b/plugins/AutoTask/v1/dataStreams/financialHealth.json @@ -1,6 +1,8 @@ { "name": "financialHealth", "displayName": "Financial Health", + "description": "Estimated revenue, cost, and hours across all contracts", + "tags": ["finance"], "baseDataSourceName": "httpRequestUnscoped", "timeframes": false, "config": { diff --git a/plugins/AutoTask/v1/dataStreams/projectStatus.json b/plugins/AutoTask/v1/dataStreams/projectStatus.json index 9c726f8..ac64a8e 100644 --- a/plugins/AutoTask/v1/dataStreams/projectStatus.json +++ b/plugins/AutoTask/v1/dataStreams/projectStatus.json @@ -1,6 +1,8 @@ { "name": "projectStatus", "displayName": "Project Status", + "description": "Status, progress, and schedule for all projects", + "tags": ["projects"], "baseDataSourceName": "httpRequestUnscoped", "timeframes": false, "config": { diff --git a/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json b/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json index 85287a2..f69e73d 100644 --- a/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json +++ b/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json @@ -1,6 +1,8 @@ { "name": "resourceUtilisation", "displayName": "Resource Utilisation", + "description": "Time entries logged by resources within the selected timeframe", + "tags": ["resources"], "baseDataSourceName": "httpRequestUnscoped", "timeframes": ["last7days", "last30days", "thisMonth", "lastMonth", "thisQuarter", "lastQuarter", "thisYear"], "config": { diff --git a/plugins/AutoTask/v1/dataStreams/scripts/tickets.js b/plugins/AutoTask/v1/dataStreams/scripts/tickets.js deleted file mode 100644 index 831759b..0000000 --- a/plugins/AutoTask/v1/dataStreams/scripts/tickets.js +++ /dev/null @@ -1,15 +0,0 @@ -var statusMap = { - 1: { name: 'New', state: 'warning' }, - 5: { name: 'Complete', state: 'success' }, - 8: { name: 'In Progress', state: 'warning' }, - 9: { name: 'Waiting Customer', state: 'unknown' }, - 10: { name: 'Waiting Materials', state: 'unknown' }, - 11: { name: 'Waiting Vendor', state: 'unknown' }, - 12: { name: 'Escalate', state: 'error' }, - 13: { name: 'Waiting Approval', state: 'unknown' } -}; - -result = (data.items || []).map(function(ticket) { - var mapped = statusMap[ticket.status] || { name: 'Status ' + ticket.status, state: 'unknown' }; - return Object.assign({}, ticket, { statusName: mapped.name, stateValue: mapped.state }); -}); diff --git a/plugins/AutoTask/v1/dataStreams/surveyResults.json b/plugins/AutoTask/v1/dataStreams/surveyResults.json new file mode 100644 index 0000000..064fcad --- /dev/null +++ b/plugins/AutoTask/v1/dataStreams/surveyResults.json @@ -0,0 +1,26 @@ +{ + "name": "surveyResults", + "displayName": "Survey Results", + "description": "Survey results submitted within the selected timeframe", + "tags": ["surveys"], + "baseDataSourceName": "httpRequestUnscoped", + "timeframes": ["last24hours", "last7days", "last30days", "thisMonth", "lastMonth", "thisQuarter", "lastQuarter", "thisYear"], + "config": { + "httpMethod": "get", + "expandInnerObjects": true, + "endpointPath": "atservicesrest/v1.0/SurveyResults/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"gte\",\"field\":\"createDate\",\"value\":\"{{timeframe.start}}\"},{\"op\":\"lte\",\"field\":\"createDate\",\"value\":\"{{timeframe.end}}\"}]}", + "pathToData": "items", + "paging": { + "mode": "nextUrl", + "pageSize": { "realm": "none" }, + "in": { + "realm": "payload", + "path": "pageDetails.nextPageUrl" + } + } + }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "visible": false }, + { "pattern": ".*" } + ] +} diff --git a/plugins/AutoTask/v1/dataStreams/tickets.json b/plugins/AutoTask/v1/dataStreams/tickets.json index e34fbdc..8e2aaeb 100644 --- a/plugins/AutoTask/v1/dataStreams/tickets.json +++ b/plugins/AutoTask/v1/dataStreams/tickets.json @@ -1,6 +1,8 @@ { "name": "tickets", "displayName": "Tickets", + "description": "Tickets with last activity within the selected timeframe", + "tags": ["tickets"], "baseDataSourceName": "httpRequestUnscoped", "timeframes": ["last24hours", "last7days", "last30days", "thisMonth", "lastMonth", "thisQuarter", "lastQuarter", "thisYear"], "config": { diff --git a/plugins/AutoTask/v1/docs/README.md b/plugins/AutoTask/v1/docs/README.md new file mode 100644 index 0000000..0c2bb32 --- /dev/null +++ b/plugins/AutoTask/v1/docs/README.md @@ -0,0 +1,32 @@ +# Before you start + +You will need an AutoTask account with admin access to create an API User. API Users are a dedicated account type in AutoTask used for system integrations — they are separate from regular user accounts and do not consume a standard user licence. + +## Creating an API User + +1. Log in to AutoTask and go to **Admin > Resources/Users (HR) > Resources** +2. Click **New** and select **API User** as the resource type +3. Complete the required fields, including an email address — this becomes the **API Username** +4. Under the **Credentials** section, click **Generate** next to **Integration Code** to create your **API Integration Code** +5. Set a **Password / Secret** — this becomes the **API Secret** +6. Save the record + +The API User must have sufficient security permissions to read the entities you want to monitor (Companies, Tickets, Contracts, Projects, Resources, and Survey Results). + +## Finding your Zone URL + +Your Zone URL is based on the AutoTask data centre your account is hosted on. To find it: + +1. Log in to AutoTask and look at the URL in your browser address bar — for example `https://ww14.autotask.net` +2. Replace `ww` with `webservices` to get the API base URL — for example `https://webservices14.autotask.net` + +Enter the full base including `https://`. + +## Credential field reference + +| Field | Where to find it | +|---|---| +| **Zone URL** | Derived from your AutoTask login URL — see above | +| **API Integration Code** | Generated on the API User record under Credentials > Integration Code | +| **API Username** | The email address entered when creating the API User | +| **API Secret** | The password set on the API User record under Credentials | diff --git a/plugins/AutoTask/v1/indexDefinitions/default.json b/plugins/AutoTask/v1/indexDefinitions/default.json index 561cd47..e662e9f 100644 --- a/plugins/AutoTask/v1/indexDefinitions/default.json +++ b/plugins/AutoTask/v1/indexDefinitions/default.json @@ -22,7 +22,7 @@ "timeframe": "none", "objectMapping": { "id": "id", - "name": "firstName", + "name": "fullName", "type": { "value": "autotask-contact" }, "properties": [ "lastName", @@ -55,7 +55,7 @@ "timeframe": "none", "objectMapping": { "id": "id", - "name": "firstName", + "name": "email", "type": { "value": "autotask-resource" }, "properties": [ "lastName", From aa930bc9dcd5ae1d9f455ff00cfa28a0949b6878 Mon Sep 17 00:00:00 2001 From: Tim Wheeler <63284593+TimWheeler-SQUP@users.noreply.github.com> Date: Sat, 2 May 2026 22:50:24 +0100 Subject: [PATCH 13/21] update tags and timeframes --- CLAUDE.local.md | 414 ++++++++++++++++++ .../AutoTask/v1/dataStreams/companies.json | 2 +- .../v1/dataStreams/contractStatus.json | 2 +- .../v1/dataStreams/financialHealth.json | 4 +- .../v1/dataStreams/projectStatus.json | 2 +- .../v1/dataStreams/resourceUtilisation.json | 4 +- .../AutoTask/v1/dataStreams/resources.json | 12 +- .../v1/dataStreams/surveyResults.json | 2 +- plugins/AutoTask/v1/dataStreams/tickets.json | 9 +- .../AutoTask/v1/indexDefinitions/default.json | 4 +- 10 files changed, 442 insertions(+), 13 deletions(-) create mode 100644 CLAUDE.local.md diff --git a/CLAUDE.local.md b/CLAUDE.local.md new file mode 100644 index 0000000..d443713 --- /dev/null +++ b/CLAUDE.local.md @@ -0,0 +1,414 @@ +# SquaredUp Plugin Authoring — Local Reference + +## What is a SquaredUp plugin +In this repo all plugins are based on the low-code plugin (LCP) framework. +This can be seen in the repo squaredup-plugin-repository under \squaredup-plugin-repository\plugins\WebAPI\v1 + +A plugin is a light weight definition of how to connect, import (or index) and display data from a 3rd party application via API into SquaredUp + +LCP plugins has it's own Confluence space - https://squaredup-eng.atlassian.net/wiki/spaces/LCP/overview + +Out-of-the-box (OOTB) dashboards can be bundled with your plugin so users get useful content immediately on installation. + +Dashboard files live in the defaultContent/ folder and follow the same format as standard SquaredUp dashboards. The captureOobContent script works the same way as for regular plugins. + +When exporting your plugin, you can optionally select dashboards to include on the Dashboards tab of the export modal. If you do, make sure those dashboards only use data streams that are included in the same export. + +## UI schema +The UI schema is used in two places in an LCP: +ui.json — the setup form shown when installing (adding) the plugin. Typically collects credentials. +ui array in data stream files — the form shown on the Parameters step when configuring a tile. + +Both use the same field definition format. + +Note: Do not set a title attribute on fields. It is not used and should be omitted. + +Example — a password field for plugin setup + +[ + { + "type": "password", + "name": "apiKey", + "label": "API key", + "help": "Create an API key in the [provider portal](https://example.com/api-keys)", + "validation": { + "required": true + }, + "placeholder": "e.g. sk_live_xxxxxxxxxxxxxxxx" + } +] +Example — a text field for data stream config + + + +[ + { + "name": "domain", + "type": "text", + "label": "Domain", + "placeholder": "mydomain.com", + "validation": { + "required": true + } + } +] +Using field values in config + +Reference field values in your config block using {{fieldName}} (Mustache syntax): + + + +{ + "baseUrl": "https://api.example.com/{{apiKey}}", + "endpointPath": "domain/{{domain}}" +} + +### Accessing plugin setup (datasource) config in a data stream + +Fields defined in `ui.json` (the plugin setup form) are accessible in data stream `endpointPath` and other config using the `datasource.` prefix: + +```json +"endpointPath": "entry/{{dataSource.userID}}" +``` + +Fields defined in a data stream's own `ui` array are referenced without the prefix: + +```json +"endpointPath": "entry/{{userID}}" +``` + +Use `{{dataSource.fieldName}}` when the value comes from plugin installation (e.g. an account ID set once for all tiles). Use `{{fieldName}}` when the value is provided per tile. In a UI section in the same .json file. + +## Terminology + +Plugin - The packaged integration (e.g. "My GitHub Plugin") + +Data source - An installed instance of a plugin, configured with credentials + +Data stream - A named query against a data source — typically maps to a single API endpoint + +Base plugin - The underlying plugin your LCP is built on (almost always Web API, but could be PowerShell or something else) + +Object indexing / import - Importing objects from your integration into the SquaredUp (knowledge) graph, provides global search and drilldown capabilities + +OOB dashboards / default content - Out-of-the-box dashboards bundled with your plugin + +## Plugin import (in-app name = indexing) +The import creates objects in a database graph for use in SquaredUp. Data streams can reference these objects. + +This is the structure of the JSON files found in the indexDefinitions folder of a low-code plugin. + +{ + "name": "vehicles", + "dataStream": { + "name": "vehicles", + "config": {} // used as dataSourceConfig - optional + }, + "timeframe": "none", + "objectMapping": { + "id": "name", + "name": "name", + // literal strings are always column names, use properties for values + "type": { "value": "starwars-vehicle" }, + "properties": [ + // add a column as an object property + "model", + "manufacturer", + "vehicle_class", + // add a column as a property with a different name + { "crewCount": "crew" }, + // convention is one property per object, but multiple also works + { "passengerCount": "passengers" }, + { "maxSpeed": "max_atmosphering_speed" }, + // add a property with a fixed value + { "owner": { "value": "Disney" } } + ] + } +} + + +## Plugins UX guidelines +Use this page when asked to review the format or naming conventions within the plugin +https://squaredup-eng.atlassian.net/wiki/spaces/SC/pages/26923138285589/Plugins+UX+Guidelines + +### Metadata.json +Every plugin has a metadata.json file at its root. This describes the plugin and controls how it appears in the catalog. + +The core fields (name, displayName, description, category, author, and icon) are set when you export from the UI. The remaining fields — notably links, keywords, and any adjustments to base.config — must be added or edited manually in the JSON after export. + +Example + + + +{ + "name": "my-plugin", + "displayName": "My Plugin", + "version": "1.0.0", + "schemaVersion": "2.0", + "category": "SquaredUp Internal", + "type": "cloud", + "author": { + "type": "community", + "name": "your-github-username" + }, + "base": { + "plugin": "WebAPI", + "majorVersion": "1", + "config": {} + }, + "links": [ + { "category": "documentation", "url": "https://github.com/squaredup/plugins/blob/main/plugins/MyPlugin/v1/docs/README.md", "label": "Help adding this plugin" }, + { "category": "source", "url": "https://github.com/squaredup/plugins/tree/main/plugins/MyPlugin/v1", "label": "Repository" } + ], + "keywords": ["monitoring", "alerts", "myservice"] +} + +### Data streams +A data stream typically maps to a single API endpoint. It defines what request to make, how to authenticate, how to present the response, and optionally how to let users configure the query at tile creation time. + +Each data stream lives in its own JSON file inside the dataStreams/ folder of your plugin. + +Basic structure + +{ + "name": "person", + "displayName": "Person", + "description": "Details for a single person", + "tags": ["people"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/people/{{personId}}", + "postRequestScript": "person-post.js" + }, + "ui": [ + { + "label": "Person ID", + "name": "personId", + "type": "text" + } + ] +} +Other properties like timeframes and metadata can also be included; defaults are inherited from the base plugin. + +Pattern-based hiding (e.g. UDF fields) +```json +{ "pattern": "udf.*", "visible": false } +``` +Place before the catch-all `{ "pattern": ".*" }`. This means that all fields not specifically listed in the data stream json file, will be mapped to columns. Without this only the defined columns are shown. + +## Base URL Pattern +- `metadata.json` `baseUrl` must be a plain `{{variable}}` — no path suffixes like `{{baseUrl}}/api/v2/` (the `/api/v2/` gets silently dropped) +- Put the full path prefix in each data stream's `endpointPath` instead: `"endpointPath": "api/v2/account/sites"` + +## Data Stream Config + +### baseDataSourceName values +| Value | Use | +|---|---| +| `httpRequestUnscoped` | No object picker — fetches globally | +| `httpRequestScoped` | Object picker — one request with all selected objects available as `{{objects}}` array | +| `httpRequestScopedSingle` | Object picker — framework automatically iterates, making one request per selected object | + +### Scoped data streams +- `matches.sourceType` constrains the object picker to a specific type +- Use `{{objects[0].propertyName}}` in `endpointPath` to reference selected object properties +- **Do not use `{{sourceId}}`** — SquaredUp's `sourceId` is prefixed with the type (e.g. `Datto Site/e63d2f98-...`), which breaks URL paths +- Use a custom property stored during import instead (e.g. `{{objects[0].siteUid}}`) +- Always include a hidden `sourceId` column in scoped data stream metadata (pattern from Pingdom): + ```json + { "name": "sourceId", "displayName": "Object ID", "shape": "string", "visible": false } + ``` + +### Paging in data streams + +Two paging mechanisms are used across plugins, depending on where the API returns the next page URL: + +**Next URL in response body** (e.g. Datto RMM — `pageDetails.nextPageUrl`): +```json +"paging": { + "mode": "nextUrl", + "pageSize": { "realm": { "value": "none", "label": "none" } }, + "in": { + "realm": { "value": "payload", "label": "payload" }, + "path": "pageDetails.nextPageUrl" + } +} +``` + +**Next URL in Link header** (e.g. GitHub — standard RFC 5988 `Link: ; rel="next"`): +```json +"paging": { + "mode": "nextUrl", + "pageSize": { + "path": "per_page", + "realm": { "value": "queryArg", "label": "Query parameter" }, + "value": "100" + }, + "in": { + "path": "next", + "realm": { "value": "webLink", "label": "Web link" } + } +} +``` + +## Metadata Shapes + +### Dates (unix timestamps in ms) +```json +{ "name": "creationDate", "shape": ["date", { "timeZone": "Etc/UTC" }] } +``` +Plain `"shape": "date"` also works for standard date strings. + +### Numbers +```json +{ "name": "count", "shape": ["number", { "decimalPlaces": 0 }] } +``` + +### State with value mapping +```json +{ + "name": "status", + "shape": ["state", { + "map": { + "success": ["Compliant", "up"], + "error": ["Not Compliant", "down"], + "warning": [], + "unknown": [], + "unmonitored": [] + } + }] +} +``` + +### Computed/derived column +```json +{ + "name": "derivedState", + "displayName": "Compliance State", + "computed": true, + "valueExpression": "{{ $['softwareStatus'] }}", + "shape": ["state", { "map": { "success": ["Compliant"], "error": ["Not Compliant"] } }] +} +``` + +### Hiding fields +```json +{ "name": "uid", "shape": "string", "visible": false } +``` + +## Known Issues / Gotchas +- `"providesPluginDiagnostics": true` fails schema validation on the squaredup-plugin-repository pipeline — avoid or remove before submitting a PR +- `*.local.json` and `*.local.md` are gitignored in this repo + +## Schemas +When creating or editing a plugin file, fetch the relevant schema URL and validate your JSON against it. + +| File | Schema URL | +|---|---| +| `configValidation.json` | https://s3.us-east-1.amazonaws.com/plugins.squaredup.saas/schemas/latest/configValidation.schema.json | +| `dataStream.json` (LCP) | https://s3.us-east-1.amazonaws.com/plugins.squaredup.saas/schemas/latest/dataStream.schema.json | +| `defaultDashboards.json` | https://s3.us-east-1.amazonaws.com/plugins.squaredup.saas/schemas/latest/defaultDashboards.schema.json | +| `metadata.json` | https://s3.us-east-1.amazonaws.com/plugins.squaredup.saas/schemas/latest/metadata.schema.json | +| `datastream.json` (WebAPI) | https://s3.us-east-1.amazonaws.com/plugins.squaredup.saas/schemas/latest/webapi/datastream.schema.json | + + +## CLI Commands +suffix is used to allow the deployment to be a unique name + +```bash +squaredup status # check login state +squaredup login --region --stage master +squaredup deploy --suffix --stage master # dev only +squaredup deploy --suffix # eu / us +squaredup delete --stage master +``` +### Custom types +JSON only — custom_types.json cannot be configured via the SquaredUp UI. It must be created and edited directly in your plugin folder. + +Custom types control how your plugin's indexed objects are displayed across SquaredUp — their label, plural form, and the icon shown in global search, the graph explorer, and object drilldown pages. + +File location + +Create a custom_types.json file at the root of your plugin folder. It is an array with one entry per object type your plugin defines. + +Example + +[ + { + "name": "My Plugin Device", + "sourceType": "my-plugin-device", + "icon": "server", + "singular": "Device", + "plural": "Devices" + } +] +Properties + +Property + +Description + +name + +An internal label for this type. Convention is " " — e.g. "UniFi Network Device" + +sourceType + +Must match the type value used in your indexDefinitions/default.json objectMapping. This is how SquaredUp links the display config to actual indexed objects + +icon + +A Lucide icon name in lowercase kebab-case (e.g. server, wifi, hard-drive, bar-chart, key, camera) + +singular + +Singular display label shown in the UI — e.g. "Device" + +plural + +Plural display label shown in the UI — e.g. "Devices" + +Relationship to object indexing + +The sourceType here must match the type value assigned in your index definition. For example: + +// indexDefinitions/default.json +"objectMapping": { + "type": { "value": "my-plugin-device" }, + ... +} + +// custom_types.json +{ "sourceType": "my-plugin-device", ... } +Without a matching entry in custom_types.json, indexed objects still appear in SquaredUp — they'll just use a generic icon and the raw sourceType string as their display label. + +Choosing icons + +Browse lucide.dev/icons to find an appropriate icon. Use the icon name in lowercase kebab-case (e.g. hard-drive, not HardDrive). + +Multiple types + +A plugin can define multiple types in the same file — one object per type: + +[ + { "name": "My Plugin Server", "sourceType": "myPlugin-server", "icon": "server", "singular": "Server", "plural": "Servers" }, + { "name": "My Plugin Database", "sourceType": "myPlugin-database", "icon": "database", "singular": "Database", "plural": "Databases" } +] +Examples + +UniFi custom_types.json — three types with different icons + +FantasyPremierLeague custom_types.json + +### Expressions +Expressions +In many areas of plugins, mustache-style expressions can be used for advanced configuration scenarios, e.g. + +Custom columns ({{ $['value'] > 99.9 ? 'success' : 'error' }}) + +Custom formatting ({{ $['value'] / 100 }}) + +Referencing objects or timeframe in Parameters ({{timeframe.start/end}}) + +Mapping UI fields to Parameters ({{myFieldName}}) \ No newline at end of file diff --git a/plugins/AutoTask/v1/dataStreams/companies.json b/plugins/AutoTask/v1/dataStreams/companies.json index bd9f8ec..4945190 100644 --- a/plugins/AutoTask/v1/dataStreams/companies.json +++ b/plugins/AutoTask/v1/dataStreams/companies.json @@ -2,7 +2,7 @@ "name": "companies", "displayName": "Companies", "description": "All active companies in AutoTask", - "tags": ["companies"], + "tags": ["Companies"], "baseDataSourceName": "httpRequestUnscoped", "timeframes": false, "config": { diff --git a/plugins/AutoTask/v1/dataStreams/contractStatus.json b/plugins/AutoTask/v1/dataStreams/contractStatus.json index c77fb12..554da68 100644 --- a/plugins/AutoTask/v1/dataStreams/contractStatus.json +++ b/plugins/AutoTask/v1/dataStreams/contractStatus.json @@ -2,7 +2,7 @@ "name": "contractStatus", "displayName": "Contract Status", "description": "Status and key dates for all contracts", - "tags": ["contracts"], + "tags": ["Contracts"], "baseDataSourceName": "httpRequestUnscoped", "timeframes": false, "config": { diff --git a/plugins/AutoTask/v1/dataStreams/financialHealth.json b/plugins/AutoTask/v1/dataStreams/financialHealth.json index d8f8440..4d7be1a 100644 --- a/plugins/AutoTask/v1/dataStreams/financialHealth.json +++ b/plugins/AutoTask/v1/dataStreams/financialHealth.json @@ -2,7 +2,7 @@ "name": "financialHealth", "displayName": "Financial Health", "description": "Estimated revenue, cost, and hours across all contracts", - "tags": ["finance"], + "tags": ["Finance"], "baseDataSourceName": "httpRequestUnscoped", "timeframes": false, "config": { @@ -20,7 +20,7 @@ } }, "metadata": [ - { "name": "id", "displayName": "ID", "visible": false }, + { "name": "id", "displayName": "ID", "visible": false, "shape":"string" }, { "name": "contractName", "displayName": "Contract Name" }, { "name": "contractType", "displayName": "Contract Type", "shape": ["number", { "decimalPlaces": 0 }] }, { "name": "estimatedRevenue", "displayName": "Estimated Revenue", "shape": ["number", { "decimalPlaces": 2 }] }, diff --git a/plugins/AutoTask/v1/dataStreams/projectStatus.json b/plugins/AutoTask/v1/dataStreams/projectStatus.json index ac64a8e..81d8d47 100644 --- a/plugins/AutoTask/v1/dataStreams/projectStatus.json +++ b/plugins/AutoTask/v1/dataStreams/projectStatus.json @@ -2,7 +2,7 @@ "name": "projectStatus", "displayName": "Project Status", "description": "Status, progress, and schedule for all projects", - "tags": ["projects"], + "tags": ["Projects"], "baseDataSourceName": "httpRequestUnscoped", "timeframes": false, "config": { diff --git a/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json b/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json index f69e73d..71a8d9a 100644 --- a/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json +++ b/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json @@ -2,7 +2,7 @@ "name": "resourceUtilisation", "displayName": "Resource Utilisation", "description": "Time entries logged by resources within the selected timeframe", - "tags": ["resources"], + "tags": ["Resources"], "baseDataSourceName": "httpRequestUnscoped", "timeframes": ["last7days", "last30days", "thisMonth", "lastMonth", "thisQuarter", "lastQuarter", "thisYear"], "config": { @@ -20,7 +20,7 @@ } }, "metadata": [ - { "name": "id", "displayName": "ID", "visible": false }, + { "name": "id", "displayName": "ID", "visible": false,"shape":"string" }, { "name": "resourceID", "displayName": "Resource ID" }, { "name": "dateWorked", "displayName": "Date Worked", "shape": ["date", { "timeZone": "Etc/UTC" }] }, { "name": "hoursWorked", "displayName": "Hours Worked", "shape": ["number", { "decimalPlaces": 2 }] }, diff --git a/plugins/AutoTask/v1/dataStreams/resources.json b/plugins/AutoTask/v1/dataStreams/resources.json index f0321e9..3a07fce 100644 --- a/plugins/AutoTask/v1/dataStreams/resources.json +++ b/plugins/AutoTask/v1/dataStreams/resources.json @@ -17,5 +17,15 @@ "path": "pageDetails.nextPageUrl" } } - } + }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string" }, + { + "name": "fullName", + "displayName": "Full Name", + "computed": true, + "valueExpression": "{{ $['firstName'] + ' ' + $['lastName'] }}" + }, + { "pattern": ".*" } + ] } diff --git a/plugins/AutoTask/v1/dataStreams/surveyResults.json b/plugins/AutoTask/v1/dataStreams/surveyResults.json index 064fcad..7ce58f3 100644 --- a/plugins/AutoTask/v1/dataStreams/surveyResults.json +++ b/plugins/AutoTask/v1/dataStreams/surveyResults.json @@ -2,7 +2,7 @@ "name": "surveyResults", "displayName": "Survey Results", "description": "Survey results submitted within the selected timeframe", - "tags": ["surveys"], + "tags": ["Surveys"], "baseDataSourceName": "httpRequestUnscoped", "timeframes": ["last24hours", "last7days", "last30days", "thisMonth", "lastMonth", "thisQuarter", "lastQuarter", "thisYear"], "config": { diff --git a/plugins/AutoTask/v1/dataStreams/tickets.json b/plugins/AutoTask/v1/dataStreams/tickets.json index 8e2aaeb..7279572 100644 --- a/plugins/AutoTask/v1/dataStreams/tickets.json +++ b/plugins/AutoTask/v1/dataStreams/tickets.json @@ -2,9 +2,9 @@ "name": "tickets", "displayName": "Tickets", "description": "Tickets with last activity within the selected timeframe", - "tags": ["tickets"], + "tags": ["Tickets"], "baseDataSourceName": "httpRequestUnscoped", - "timeframes": ["last24hours", "last7days", "last30days", "thisMonth", "lastMonth", "thisQuarter", "lastQuarter", "thisYear"], + "timeframes": true, "config": { "httpMethod": "get", "expandInnerObjects": true, @@ -22,6 +22,7 @@ "metadata": [ { "name": "id", "displayName": "Ticket ID", "shape": "string" }, { "name": "title", "displayName": "Title" }, + { "name": "description", "displayName": "Description", "visible": false }, { "name": "statusName", "displayName": "Status", @@ -43,8 +44,10 @@ { "name": "contactName", "displayName": "Contact", "sourceId": "contactID", "objectPropertyPath": "name" }, { "name": "assignedResourceID", "sourceType": "autotask-resource", "visible": false }, { "name": "assignedResourceName", "displayName": "Assigned Resource", "sourceId": "assignedResourceID", "objectPropertyPath": "name" }, + { "name": "completedByResourceID", "sourceType": "autotask-resource", "visible": false }, + { "name": "completedByResourceName", "displayName": "Completed By", "sourceId": "completedByResourceID", "objectPropertyPath": "name" }, { "name": "lastActivityDate", "displayName": "Last Activity Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, { "name": "dueDateTime", "displayName": "Due Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, - { "pattern": ".*" } + { "pattern": ".*", "visible": false } ] } diff --git a/plugins/AutoTask/v1/indexDefinitions/default.json b/plugins/AutoTask/v1/indexDefinitions/default.json index e662e9f..5dba932 100644 --- a/plugins/AutoTask/v1/indexDefinitions/default.json +++ b/plugins/AutoTask/v1/indexDefinitions/default.json @@ -25,6 +25,7 @@ "name": "fullName", "type": { "value": "autotask-contact" }, "properties": [ + "firstName", "lastName", "emailAddress", "phone", @@ -55,9 +56,10 @@ "timeframe": "none", "objectMapping": { "id": "id", - "name": "email", + "name": "fullName", "type": { "value": "autotask-resource" }, "properties": [ + "firstName", "lastName", "email", "isActive", From c20b3660319d44f2f1ce71e75bb9ec103a1946e1 Mon Sep 17 00:00:00 2001 From: Tim Wheeler <63284593+TimWheeler-SQUP@users.noreply.github.com> Date: Sun, 3 May 2026 23:07:59 +0100 Subject: [PATCH 14/21] Add OOB dashboard --- plugins/AutoTask/v1/dataStreams/tickets.json | 16 +- .../AutoTask/v1/defaultContent/manifest.json | 8 + .../v1/defaultContent/tickets.dash.json | 446 ++++++++++++++++++ plugins/AutoTask/v1/metadata.json | 2 +- 4 files changed, 467 insertions(+), 5 deletions(-) create mode 100644 plugins/AutoTask/v1/defaultContent/manifest.json create mode 100644 plugins/AutoTask/v1/defaultContent/tickets.dash.json diff --git a/plugins/AutoTask/v1/dataStreams/tickets.json b/plugins/AutoTask/v1/dataStreams/tickets.json index 7279572..8ede654 100644 --- a/plugins/AutoTask/v1/dataStreams/tickets.json +++ b/plugins/AutoTask/v1/dataStreams/tickets.json @@ -38,14 +38,22 @@ }] }, { "name": "priority", "displayName": "Priority", "shape": ["number", { "decimalPlaces": 0 }] }, - { "name": "companyID", "sourceType": "autotask-company", "visible": false }, + { "name": "companyID", "sourceType": "autotask-company", "shape": "string", "visible": false }, { "name": "companyName", "displayName": "Company", "sourceId": "companyID", "objectPropertyPath": "name" }, - { "name": "contactID", "sourceType": "autotask-contact", "visible": false }, + { "name": "contactID", "sourceType": "autotask-contact", "shape": "string", "visible": false }, { "name": "contactName", "displayName": "Contact", "sourceId": "contactID", "objectPropertyPath": "name" }, - { "name": "assignedResourceID", "sourceType": "autotask-resource", "visible": false }, + { "name": "contractID", "sourceType": "autotask-contract", "shape": "string", "visible": false }, + { "name": "contractName", "displayName": "Contract", "sourceId": "contractID", "objectPropertyPath": "name" }, + { "name": "assignedResourceID", "sourceType": "autotask-resource", "shape": "string", "visible": false }, { "name": "assignedResourceName", "displayName": "Assigned Resource", "sourceId": "assignedResourceID", "objectPropertyPath": "name" }, - { "name": "completedByResourceID", "sourceType": "autotask-resource", "visible": false }, + { "name": "completedByResourceID", "sourceType": "autotask-resource", "shape": "string", "visible": false }, { "name": "completedByResourceName", "displayName": "Completed By", "sourceId": "completedByResourceID", "objectPropertyPath": "name" }, + { "name": "firstresponeassignedresourceID", "sourceType": "autotask-resource", "shape": "string", "visible": false }, + { "name": "firstResponseAssignedResourceName", "displayName": "First Response Resource", "sourceId": "firstresponeassignedresourceID", "objectPropertyPath": "name" }, + { "name": "LastActivityResourceID", "sourceType": "autotask-resource", "shape": "string", "visible": false }, + { "name": "lastActivityResourceName", "displayName": "Last Activity Resource", "sourceId": "LastActivityResourceID", "objectPropertyPath": "name" }, + { "name": "CreatorResourceID", "sourceType": "autotask-resource", "shape": "string", "visible": false }, + { "name": "creatorResourceName", "displayName": "Creator", "sourceId": "CreatorResourceID", "objectPropertyPath": "name" }, { "name": "lastActivityDate", "displayName": "Last Activity Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, { "name": "dueDateTime", "displayName": "Due Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, { "pattern": ".*", "visible": false } diff --git a/plugins/AutoTask/v1/defaultContent/manifest.json b/plugins/AutoTask/v1/defaultContent/manifest.json new file mode 100644 index 0000000..bd848d1 --- /dev/null +++ b/plugins/AutoTask/v1/defaultContent/manifest.json @@ -0,0 +1,8 @@ +{ + "items": [ + { + "name": "tickets", + "type": "dashboard" + } + ] +} diff --git a/plugins/AutoTask/v1/defaultContent/tickets.dash.json b/plugins/AutoTask/v1/defaultContent/tickets.dash.json new file mode 100644 index 0000000..43036b2 --- /dev/null +++ b/plugins/AutoTask/v1/defaultContent/tickets.dash.json @@ -0,0 +1,446 @@ +{ + "name": "Tickets", + "schemaVersion": "1.4", + "dashboard": { + "_type": "layout/grid", + "contents": [ + { + "static": false, + "w": 1, + "moved": false, + "h": 1, + "x": 0, + "y": 0, + "i": "35674a0b-dd87-4845-a083-33fac61b20a8", + "z": 0, + "config": { + "timeframe": "last30days", + "dataStream": { + "name": "tickets", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "dueDateTime", + "tenseUnit": "before", + "unit": "minutes", + "operation": "datemorethan", + "value": "1" + }, + { + "column": "statusName", + "operation": "notequals", + "value": "success" + } + ] + }, + "id": "{{dataStreams.tickets}}" + }, + "scope": { + "query": "g.V().order().by('__name').hasNot('__canonicalType').has(\"__configId\", within(\"{{configId}}\")).limit(500)", + "bindings": {}, + "queryDetail": {} + }, + "_type": "tile/data-stream", + "description": "", + "monitor": { + "tileRollsUp": true, + "monitorType": "threshold", + "condition": { + "columns": [ + "apiVendorID" + ], + "logic": { + "if": [ + { + ">": [ + { + "var": "top" + }, + 0 + ] + }, + "error" + ] + } + }, + "_type": "simple", + "column": "apiVendorID", + "aggregation": "top", + "groupBy": "__group_by_none__", + "frequency": 720 + }, + "activePluginConfigIds": [ + "{{configId}}" + ], + "title": "Overdue Tickets", + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": { + "type": "count" + }, + "comparisonColumn": "none" + } + } + } + } + }, + { + "static": false, + "w": 1, + "moved": false, + "h": 1, + "x": 1, + "y": 0, + "i": "99af9bf5-ac61-4546-a13f-d2cd838fd3b6", + "z": 0, + "config": { + "timeframe": "last30days", + "dataStream": { + "name": "tickets", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "status", + "operation": "notequals", + "value": "5" + } + ] + }, + "id": "{{dataStreams.tickets}}" + }, + "scope": { + "query": "g.V().order().by('__name').hasNot('__canonicalType').has(\"__configId\", within(\"{{configId}}\")).limit(500)", + "bindings": {}, + "queryDetail": {} + }, + "_type": "tile/data-stream", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "title": "Open Tickets", + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": { + "type": "count" + }, + "comparisonColumn": "none" + } + } + } + } + }, + { + "static": false, + "w": 1, + "moved": false, + "h": 1, + "x": 2, + "y": 0, + "i": "353ede63-eccf-433d-91c0-5f6a2dc33af5", + "z": 0, + "config": { + "dataStream": { + "dataSourceConfig": { + "version": "2.0", + "tables": [ + { + "config": { + "timeframe": "last7days", + "activePluginConfigIds": [ + "{{configId}}" + ], + "scope": { + "query": "g.V().order().by('__name').hasNot('__canonicalType').has(\"__configId\", within(\"{{configId}}\")).limit(500)", + "bindings": {}, + "queryDetail": {} + }, + "dataStream": { + "name": "tickets", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "statusName", + "operation": "equals", + "value": "success" + }, + { + "column": "createDate", + "unit": "days", + "operation": "datewithinlast", + "value": "30" + } + ] + }, + "id": "{{dataStreams.tickets}}" + } + }, + "tableName": "dataset1" + } + ], + "sql": "SELECT\r\n ROUND( AVG(DATE_DIFF('hour', \"createDate\",\"firstResponseDateTime\")),2) AS \"avg_hours_difference\"\r\nFROM\r\n \"dataset1\"" + }, + "id": "datastream-sql" + }, + "scope": { + "query": "g.V().order().by('__name').hasNot('__canonicalType').has(\"__configId\", within(\"{{configId}}\")).limit(500)", + "bindings": {}, + "queryDetail": {} + }, + "_type": "tile/data-stream", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "title": "Avg First Response Time", + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "avg_hours_difference", + "comparisonColumn": "none", + "label": "Hours" + } + } + } + } + }, + { + "static": false, + "w": 1, + "moved": false, + "h": 1, + "x": 3, + "y": 0, + "i": "113d7b96-e608-4dca-82f8-fa93ff880528", + "z": 0, + "config": { + "dataStream": { + "dataSourceConfig": { + "version": "2.0", + "tables": [ + { + "config": { + "timeframe": "last7days", + "activePluginConfigIds": [ + "{{configId}}" + ], + "scope": { + "query": "g.V().order().by('__name').hasNot('__canonicalType').has(\"__configId\", within(\"{{configId}}\")).limit(500)", + "bindings": {}, + "queryDetail": {} + }, + "dataStream": { + "name": "tickets", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "statusName", + "operation": "equals", + "value": "success" + } + ] + }, + "id": "{{dataStreams.tickets}}" + } + }, + "tableName": "dataset1" + } + ], + "sql": "WITH\r\n \"resolution_times\" AS (\r\n SELECT\r\n CAST(\r\n EXTRACT (\r\n EPOCH\r\n FROM\r\n \"resolvedDateTime\" - \"createDate\"\r\n ) AS INTEGER\r\n ) / 3600 AS \"resolution_time_hours\"\r\n FROM\r\n \"dataset1\"\r\n )\r\nSELECT\r\n ROUND(AVG(\"resolution_time_hours\"),2)\r\nFROM\r\n \"resolution_times\"" + }, + "id": "datastream-sql" + }, + "scope": { + "query": "g.V().order().by('__name').hasNot('__canonicalType').has(\"__configId\", within(\"{{configId}}\")).limit(500)", + "bindings": {}, + "queryDetail": {} + }, + "_type": "tile/data-stream", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "title": "Avg Time to Resolution", + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "comparisonColumn": "none", + "label": "Hours", + "value": "avg(resolution_time_hours)", + "formatted": true + } + } + } + } + }, + { + "static": false, + "w": 2, + "moved": false, + "h": 3, + "x": 0, + "y": 1, + "i": "73f1150f-0a49-49be-974f-f2da0e33a0b5", + "z": 0, + "config": { + "timeframe": "last30days", + "dataStream": { + "name": "tickets", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "resolvedDateTime", + "operation": "empty" + } + ] + }, + "id": "{{dataStreams.tickets}}", + "group": { + "by": [ + [ + "assignedResourceName", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "scope": { + "query": "g.V().order().by('__name').hasNot('__canonicalType').has(\"__configId\", within(\"{{configId}}\")).limit(500)", + "bindings": {}, + "queryDetail": {} + }, + "_type": "tile/data-stream", + "description": "Open tickets", + "activePluginConfigIds": [ + "{{configId}}" + ], + "title": "Tickets per Assigned Resource", + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisGroup": "none", + "showLegend": false, + "range": { + "type": "auto" + }, + "showGrid": true, + "grouping": false, + "displayMode": "actual", + "xAxisData": "assignedResourceName_uniqueValues", + "showTotals": false, + "yAxisLabel": "", + "horizontalLayout": "vertical", + "showValue": false, + "yAxisData": [ + "count" + ], + "showYAxisLabel": true, + "xAxisLabel": "", + "legendPosition": "bottom", + "showXAxisLabel": true + } + } + } + } + }, + { + "static": false, + "w": 2, + "moved": false, + "h": 3, + "x": 2, + "y": 1, + "i": "15c45143-76f4-4d64-93e1-5e270dd78396", + "z": 0, + "config": { + "timeframe": "last7days", + "dataStream": { + "name": "tickets", + "filter": { + "multiOperation": "and", + "filters": [ + { + "column": "resolvedDateTime", + "operation": "notempty" + } + ] + }, + "id": "{{dataStreams.tickets}}", + "group": { + "by": [ + [ + "assignedResourceName", + "uniqueValues" + ] + ], + "aggregate": [ + { + "type": "count" + } + ] + } + }, + "scope": { + "query": "g.V().order().by('__name').hasNot('__canonicalType').has(\"__configId\", within(\"{{configId}}\")).limit(500)", + "bindings": {}, + "queryDetail": {} + }, + "_type": "tile/data-stream", + "description": "", + "activePluginConfigIds": [ + "{{configId}}" + ], + "title": "Tickets Closed per Resource", + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisGroup": "none", + "showLegend": false, + "range": { + "type": "auto" + }, + "showGrid": true, + "grouping": false, + "displayMode": "actual", + "xAxisData": "assignedResourceName_uniqueValues", + "showTotals": false, + "yAxisLabel": "", + "horizontalLayout": "vertical", + "showValue": false, + "yAxisData": [ + "count" + ], + "showYAxisLabel": true, + "xAxisLabel": "", + "legendPosition": "bottom", + "showXAxisLabel": true + } + } + } + } + } + ], + "version": 78, + "columns": 4 + }, + "path": "tickets", + "folderPath": [] +} diff --git a/plugins/AutoTask/v1/metadata.json b/plugins/AutoTask/v1/metadata.json index e6bb723..ea4c807 100644 --- a/plugins/AutoTask/v1/metadata.json +++ b/plugins/AutoTask/v1/metadata.json @@ -6,7 +6,7 @@ "name": "@TimWheeler-SQUP", "type": "community" }, - "description": "AutoTask - Companies, Tickets, SLAs and Billing", + "description": "AutoTask - Dashboard Companies, Tickets, Resources and Contacts", "category": "User Defined", "type": "hybrid", "schemaVersion": "2.0", From e4b0b440f9819908b34202d5b07ecd7b0c6ed704 Mon Sep 17 00:00:00 2001 From: Tim Wheeler <63284593+TimWheeler-SQUP@users.noreply.github.com> Date: Mon, 4 May 2026 10:55:39 +0100 Subject: [PATCH 15/21] Fix survey results and update contractStatus data streams --- .../v1/dataStreams/contractStatus.json | 17 +++++++++------ .../v1/dataStreams/surveyResults.json | 2 +- .../v1/defaultContent/tickets.dash.json | 12 +++-------- .../AutoTask/v1/indexDefinitions/default.json | 3 ++- plugins/AutoTask/v1/ui.json | 21 ++++++++++--------- 5 files changed, 28 insertions(+), 27 deletions(-) diff --git a/plugins/AutoTask/v1/dataStreams/contractStatus.json b/plugins/AutoTask/v1/dataStreams/contractStatus.json index 554da68..af753a3 100644 --- a/plugins/AutoTask/v1/dataStreams/contractStatus.json +++ b/plugins/AutoTask/v1/dataStreams/contractStatus.json @@ -1,15 +1,18 @@ { "name": "contractStatus", "displayName": "Contract Status", - "description": "Status and key dates for all contracts", + "description": "Status and key dates for contracts belonging to the selected company", "tags": ["Contracts"], - "baseDataSourceName": "httpRequestUnscoped", + "baseDataSourceName": "httpRequestScopedSingle", "timeframes": false, "config": { "httpMethod": "get", "expandInnerObjects": true, - "endpointPath": "atservicesrest/v1.0/Contracts/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"exist\",\"field\":\"id\"}]}", + "endpointPath": "atservicesrest/v1.0/Contracts/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"eq\",\"field\":\"companyID\",\"value\":{{objects[0].companyId}}}]}", "pathToData": "items", + "matches": { + "sourceType": "autotask-company" + }, "paging": { "mode": "nextUrl", "pageSize": { "realm": "none" }, @@ -20,13 +23,15 @@ } }, "metadata": [ - { "name": "id", "displayName": "ID", "visible": false }, - { "name": "contractName", "displayName": "Contract Name" }, + { "name": "sourceId", "displayName": "Object ID", "shape": "string", "visible": false }, + { "name": "id", "displayName": "ID", "sourceType": "autotask-contract", "shape": "string", "visible": false }, + { "name": "contractName", "displayName": "Contract Name", "sourceId": "id", "objectPropertyPath": "name" }, + { "name": "companyID", "displayName": "Company ID", "sourceType": "autotask-company", "shape": "string", "visible": false }, + { "name": "companyName", "displayName": "Company", "sourceId": "companyID", "objectPropertyPath": "name" }, { "name": "contractType", "displayName": "Contract Type", "shape": ["number", { "decimalPlaces": 0 }] }, { "name": "status", "displayName": "Status", "shape": ["number", { "decimalPlaces": 0 }] }, { "name": "startDate", "displayName": "Start Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, { "name": "endDate", "displayName": "End Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, - { "name": "companyID", "displayName": "Company ID" }, { "pattern": ".*" } ] } diff --git a/plugins/AutoTask/v1/dataStreams/surveyResults.json b/plugins/AutoTask/v1/dataStreams/surveyResults.json index 7ce58f3..80348b5 100644 --- a/plugins/AutoTask/v1/dataStreams/surveyResults.json +++ b/plugins/AutoTask/v1/dataStreams/surveyResults.json @@ -8,7 +8,7 @@ "config": { "httpMethod": "get", "expandInnerObjects": true, - "endpointPath": "atservicesrest/v1.0/SurveyResults/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"gte\",\"field\":\"createDate\",\"value\":\"{{timeframe.start}}\"},{\"op\":\"lte\",\"field\":\"createDate\",\"value\":\"{{timeframe.end}}\"}]}", + "endpointPath": "atservicesrest/v1.0/SurveyResults/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"gte\",\"field\":\"completeDate\",\"value\":\"{{timeframe.start}}\"},{\"op\":\"lte\",\"field\":\"completeDate\",\"value\":\"{{timeframe.end}}\"}]}", "pathToData": "items", "paging": { "mode": "nextUrl", diff --git a/plugins/AutoTask/v1/defaultContent/tickets.dash.json b/plugins/AutoTask/v1/defaultContent/tickets.dash.json index 43036b2..032e4e9 100644 --- a/plugins/AutoTask/v1/defaultContent/tickets.dash.json +++ b/plugins/AutoTask/v1/defaultContent/tickets.dash.json @@ -1,5 +1,5 @@ { - "name": "Tickets", + "name": "Tickets Overview", "schemaVersion": "1.4", "dashboard": { "_type": "layout/grid", @@ -14,7 +14,7 @@ "i": "35674a0b-dd87-4845-a083-33fac61b20a8", "z": 0, "config": { - "timeframe": "last30days", + "timeframe": "last7days", "dataStream": { "name": "tickets", "filter": { @@ -97,7 +97,7 @@ "i": "99af9bf5-ac61-4546-a13f-d2cd838fd3b6", "z": 0, "config": { - "timeframe": "last30days", + "timeframe": "last7days", "dataStream": { "name": "tickets", "filter": { @@ -170,12 +170,6 @@ "column": "statusName", "operation": "equals", "value": "success" - }, - { - "column": "createDate", - "unit": "days", - "operation": "datewithinlast", - "value": "30" } ] }, diff --git a/plugins/AutoTask/v1/indexDefinitions/default.json b/plugins/AutoTask/v1/indexDefinitions/default.json index 5dba932..d29a6bf 100644 --- a/plugins/AutoTask/v1/indexDefinitions/default.json +++ b/plugins/AutoTask/v1/indexDefinitions/default.json @@ -12,7 +12,8 @@ "companyType", "phone", "lastActivityDate", - "isActive" + "isActive", + { "companyId": "id" } ] } }, diff --git a/plugins/AutoTask/v1/ui.json b/plugins/AutoTask/v1/ui.json index 5fbf227..fbaac11 100644 --- a/plugins/AutoTask/v1/ui.json +++ b/plugins/AutoTask/v1/ui.json @@ -6,16 +6,8 @@ "help": "Your AutoTask zone URL — found in the address bar when logged into AutoTask, e.g. if your URL is `https://ww14.autotask.net` enter `https://webservices14.autotask.net`", "placeholder": "https://webservices16.autotask.net", "validation": { - "required": true - } - }, - { - "type": "password", - "name": "integrationCode", - "label": "API Integration Code", - "help": "Generated when creating an API User in AutoTask — Admin > Resources > API User", - "validation": { - "required": true + "required": true, + "pattern": "^.*[^/]$" } }, { @@ -28,6 +20,15 @@ "required": true } }, + { + "type": "password", + "name": "integrationCode", + "label": "API Integration Code", + "help": "Generated when creating an API User in AutoTask — Admin > Resources > API User", + "validation": { + "required": true + } + }, { "type": "password", "name": "secret", From ca0bc31d1f8fa4b7a12b6d36ca02e13956da3974 Mon Sep 17 00:00:00 2001 From: Tim Wheeler <63284593+TimWheeler-SQUP@users.noreply.github.com> Date: Tue, 5 May 2026 18:30:12 +0100 Subject: [PATCH 16/21] Improve column mappings and status values. Fix OOB dashboard --- CLAUDE.local.md | 1 + .../v1/dataStreams/contractStatus.json | 22 +++++++++++++++++-- .../v1/dataStreams/financialHealth.json | 22 +++++++++++++++++-- .../v1/dataStreams/resourceUtilisation.json | 3 ++- plugins/AutoTask/v1/dataStreams/tickets.json | 1 + .../v1/defaultContent/tickets.dash.json | 18 ++++++--------- plugins/AutoTask/v1/metadata.json | 1 - 7 files changed, 51 insertions(+), 17 deletions(-) diff --git a/CLAUDE.local.md b/CLAUDE.local.md index d443713..b6ab359 100644 --- a/CLAUDE.local.md +++ b/CLAUDE.local.md @@ -299,6 +299,7 @@ Plain `"shape": "date"` also works for standard date strings. ## Known Issues / Gotchas - `"providesPluginDiagnostics": true` fails schema validation on the squaredup-plugin-repository pipeline — avoid or remove before submitting a PR +- `"importNotSupported": false` is not a recognised `metadata.json` field and will also fail schema validation — remove it. Import is enabled by default whenever `indexDefinitions/` is present; no flag needed to enable it. - `*.local.json` and `*.local.md` are gitignored in this repo ## Schemas diff --git a/plugins/AutoTask/v1/dataStreams/contractStatus.json b/plugins/AutoTask/v1/dataStreams/contractStatus.json index af753a3..75a7d97 100644 --- a/plugins/AutoTask/v1/dataStreams/contractStatus.json +++ b/plugins/AutoTask/v1/dataStreams/contractStatus.json @@ -28,8 +28,26 @@ { "name": "contractName", "displayName": "Contract Name", "sourceId": "id", "objectPropertyPath": "name" }, { "name": "companyID", "displayName": "Company ID", "sourceType": "autotask-company", "shape": "string", "visible": false }, { "name": "companyName", "displayName": "Company", "sourceId": "companyID", "objectPropertyPath": "name" }, - { "name": "contractType", "displayName": "Contract Type", "shape": ["number", { "decimalPlaces": 0 }] }, - { "name": "status", "displayName": "Status", "shape": ["number", { "decimalPlaces": 0 }] }, + { + "name": "contractTypeName", + "displayName": "Contract Type", + "computed": true, + "valueExpression": "{{ $['contractType'] == 1 ? 'Time and Materials' : $['contractType'] == 3 ? 'Fixed Price' : $['contractType'] == 4 ? 'Block Hours' : $['contractType'] == 6 ? 'Retainer' : $['contractType'] == 7 ? 'Recurring Service' : $['contractType'] == 8 ? 'Incident' : 'Unknown' }}" + }, + { + "name": "statusName", + "displayName": "Status", + "computed": true, + "valueExpression": "{{ $['status'] == 1 ? 'Active' : $['status'] == 2 ? 'Upcoming' : $['status'] == 3 ? 'Cancelled' : $['status'] == 4 ? 'Completed' : $['status'] == 6 ? 'Inactive' : 'Unknown' }}", + "shape": ["state", { + "map": { + "success": ["Active", "Completed"], + "error": ["Cancelled"], + "warning": ["Upcoming"], + "unknown": ["Inactive", "Unknown"] + } + }] + }, { "name": "startDate", "displayName": "Start Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, { "name": "endDate", "displayName": "End Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, { "pattern": ".*" } diff --git a/plugins/AutoTask/v1/dataStreams/financialHealth.json b/plugins/AutoTask/v1/dataStreams/financialHealth.json index 4d7be1a..83f9bc8 100644 --- a/plugins/AutoTask/v1/dataStreams/financialHealth.json +++ b/plugins/AutoTask/v1/dataStreams/financialHealth.json @@ -22,11 +22,29 @@ "metadata": [ { "name": "id", "displayName": "ID", "visible": false, "shape":"string" }, { "name": "contractName", "displayName": "Contract Name" }, - { "name": "contractType", "displayName": "Contract Type", "shape": ["number", { "decimalPlaces": 0 }] }, + { + "name": "contractTypeName", + "displayName": "Contract Type", + "computed": true, + "valueExpression": "{{ $['contractType'] == 1 ? 'Time and Materials' : $['contractType'] == 3 ? 'Fixed Price' : $['contractType'] == 4 ? 'Block Hours' : $['contractType'] == 6 ? 'Retainer' : $['contractType'] == 7 ? 'Recurring Service' : $['contractType'] == 8 ? 'Incident' : 'Unknown' }}" + }, { "name": "estimatedRevenue", "displayName": "Estimated Revenue", "shape": ["number", { "decimalPlaces": 2 }] }, { "name": "estimatedCost", "displayName": "Estimated Cost", "shape": ["number", { "decimalPlaces": 2 }] }, { "name": "estimatedHours", "displayName": "Estimated Hours", "shape": ["number", { "decimalPlaces": 2 }] }, - { "name": "status", "displayName": "Status", "shape": ["number", { "decimalPlaces": 0 }] }, + { + "name": "statusName", + "displayName": "Status", + "computed": true, + "valueExpression": "{{ $['status'] == 1 ? 'Active' : $['status'] == 2 ? 'Upcoming' : $['status'] == 3 ? 'Cancelled' : $['status'] == 4 ? 'Completed' : $['status'] == 6 ? 'Inactive' : 'Unknown' }}", + "shape": ["state", { + "map": { + "success": ["Active", "Completed"], + "error": ["Cancelled"], + "warning": ["Upcoming"], + "unknown": ["Inactive", "Unknown"] + } + }] + }, { "pattern": ".*" } ] } diff --git a/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json b/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json index 71a8d9a..9147f5a 100644 --- a/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json +++ b/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json @@ -21,7 +21,8 @@ }, "metadata": [ { "name": "id", "displayName": "ID", "visible": false,"shape":"string" }, - { "name": "resourceID", "displayName": "Resource ID" }, + { "name": "resourceID", "sourceType": "autotask-resource", "shape": "string", "visible": false }, + { "name": "resourceName", "displayName": "Resource", "sourceId": "resourceID", "objectPropertyPath": "name" }, { "name": "dateWorked", "displayName": "Date Worked", "shape": ["date", { "timeZone": "Etc/UTC" }] }, { "name": "hoursWorked", "displayName": "Hours Worked", "shape": ["number", { "decimalPlaces": 2 }] }, { "name": "hoursToBill", "displayName": "Hours to Bill", "shape": ["number", { "decimalPlaces": 2 }] }, diff --git a/plugins/AutoTask/v1/dataStreams/tickets.json b/plugins/AutoTask/v1/dataStreams/tickets.json index 8ede654..6d017b9 100644 --- a/plugins/AutoTask/v1/dataStreams/tickets.json +++ b/plugins/AutoTask/v1/dataStreams/tickets.json @@ -26,6 +26,7 @@ { "name": "statusName", "displayName": "Status", + "visible": true, "computed": true, "valueExpression": "{{ $['status'] == 1 ? 'New' : $['status'] == 5 ? 'Complete' : $['status'] == 8 ? 'In Progress' : $['status'] == 9 ? 'Waiting Customer' : $['status'] == 10 ? 'Waiting Materials' : $['status'] == 11 ? 'Waiting Vendor' : $['status'] == 12 ? 'Escalate' : $['status'] == 13 ? 'Waiting Approval' : 'Unknown' }}", "shape": ["state", { diff --git a/plugins/AutoTask/v1/defaultContent/tickets.dash.json b/plugins/AutoTask/v1/defaultContent/tickets.dash.json index 032e4e9..2cd934a 100644 --- a/plugins/AutoTask/v1/defaultContent/tickets.dash.json +++ b/plugins/AutoTask/v1/defaultContent/tickets.dash.json @@ -46,16 +46,16 @@ "monitor": { "tileRollsUp": true, "monitorType": "threshold", - "condition": { - "columns": [ - "apiVendorID" - ], + "aggregation": "count", + "groupBy": "__group_by_none__", + "condition": { + "columns": [], "logic": { "if": [ { ">": [ { - "var": "top" + "var": "count" }, 0 ] @@ -65,9 +65,6 @@ } }, "_type": "simple", - "column": "apiVendorID", - "aggregation": "top", - "groupBy": "__group_by_none__", "frequency": 720 }, "activePluginConfigIds": [ @@ -249,7 +246,7 @@ "tableName": "dataset1" } ], - "sql": "WITH\r\n \"resolution_times\" AS (\r\n SELECT\r\n CAST(\r\n EXTRACT (\r\n EPOCH\r\n FROM\r\n \"resolvedDateTime\" - \"createDate\"\r\n ) AS INTEGER\r\n ) / 3600 AS \"resolution_time_hours\"\r\n FROM\r\n \"dataset1\"\r\n )\r\nSELECT\r\n ROUND(AVG(\"resolution_time_hours\"),2)\r\nFROM\r\n \"resolution_times\"" + "sql": "WITH\r\n \"resolution_times\" AS (\r\n SELECT\r\n CAST(\r\n EXTRACT (\r\n EPOCH\r\n FROM\r\n \"resolvedDateTime\" - \"createDate\"\r\n ) AS INTEGER\r\n ) / 3600 AS \"resolution_time_hours\"\r\n FROM\r\n \"dataset1\"\r\n )\r\nSELECT\r\n ROUND(AVG(\"resolution_time_hours\"),2) AS \"avg_resolution_hours\"\r\nFROM\r\n \"resolution_times\"" }, "id": "datastream-sql" }, @@ -270,8 +267,7 @@ "data-stream-scalar": { "comparisonColumn": "none", "label": "Hours", - "value": "avg(resolution_time_hours)", - "formatted": true + "value": "avg_resolution_hours" } } } diff --git a/plugins/AutoTask/v1/metadata.json b/plugins/AutoTask/v1/metadata.json index ea4c807..45cd30a 100644 --- a/plugins/AutoTask/v1/metadata.json +++ b/plugins/AutoTask/v1/metadata.json @@ -10,7 +10,6 @@ "category": "User Defined", "type": "hybrid", "schemaVersion": "2.0", - "importNotSupported": false, "base": { "plugin": "WebAPI", "majorVersion": "1", From 9c26e59ecd2e0ec5c44735e12bfc451f2cc46fda Mon Sep 17 00:00:00 2001 From: Tim Wheeler <63284593+TimWheeler-SQUP@users.noreply.github.com> Date: Wed, 6 May 2026 10:08:19 +0100 Subject: [PATCH 17/21] updated plugin type and fixed contractStatus --- plugins/AutoTask/v1/dataStreams/contractStatus.json | 6 +++--- plugins/AutoTask/v1/metadata.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/AutoTask/v1/dataStreams/contractStatus.json b/plugins/AutoTask/v1/dataStreams/contractStatus.json index 75a7d97..bfe1995 100644 --- a/plugins/AutoTask/v1/dataStreams/contractStatus.json +++ b/plugins/AutoTask/v1/dataStreams/contractStatus.json @@ -5,14 +5,14 @@ "tags": ["Contracts"], "baseDataSourceName": "httpRequestScopedSingle", "timeframes": false, + "matches": { + "sourceType": "autotask-company" + }, "config": { "httpMethod": "get", "expandInnerObjects": true, "endpointPath": "atservicesrest/v1.0/Contracts/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"eq\",\"field\":\"companyID\",\"value\":{{objects[0].companyId}}}]}", "pathToData": "items", - "matches": { - "sourceType": "autotask-company" - }, "paging": { "mode": "nextUrl", "pageSize": { "realm": "none" }, diff --git a/plugins/AutoTask/v1/metadata.json b/plugins/AutoTask/v1/metadata.json index 45cd30a..80d1f12 100644 --- a/plugins/AutoTask/v1/metadata.json +++ b/plugins/AutoTask/v1/metadata.json @@ -8,7 +8,7 @@ }, "description": "AutoTask - Dashboard Companies, Tickets, Resources and Contacts", "category": "User Defined", - "type": "hybrid", + "type": "cloud", "schemaVersion": "2.0", "base": { "plugin": "WebAPI", From 4b7ece8da0bdf511b2e4d10d31120396c2c30148 Mon Sep 17 00:00:00 2001 From: Tim Wheeler <63284593+TimWheeler-SQUP@users.noreply.github.com> Date: Sat, 9 May 2026 13:48:08 +0100 Subject: [PATCH 18/21] updated terminology and added object and parameter step to Tickets --- .../AutoTask/v1/dataStreams/companies.json | 16 ++- .../v1/dataStreams/contractStatus.json | 2 +- .../v1/dataStreams/resourceUtilisation.json | 2 +- plugins/AutoTask/v1/dataStreams/tickets.json | 124 +++++++++++++++++- plugins/AutoTask/v1/metadata.json | 2 +- test-present.txt | 1 + 6 files changed, 138 insertions(+), 9 deletions(-) create mode 100644 test-present.txt diff --git a/plugins/AutoTask/v1/dataStreams/companies.json b/plugins/AutoTask/v1/dataStreams/companies.json index 4945190..1eccdff 100644 --- a/plugins/AutoTask/v1/dataStreams/companies.json +++ b/plugins/AutoTask/v1/dataStreams/companies.json @@ -18,5 +18,19 @@ "path": "pageDetails.nextPageUrl" } } - } + }, + "metadata": [ + { "name": "companyName", "displayName": "Company Name" }, + { "name": "isActive", "displayName": "Is Active" }, + { "name": "phone", "displayName": "Phone" }, + { "name": "address1", "displayName": "Address" }, + { "name": "city", "displayName": "City" }, + { "name": "state", "displayName": "State" }, + { "name": "postalCode", "displayName": "Postal Code" }, + { "name": "country", "displayName": "Country" }, + { "name": "createDate", "displayName": "Created Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "ownerResourceID", "sourceType": "autotask-resource", "shape": "string", "visible": false }, + { "name": "ownerResourceName", "displayName": "Owner", "sourceId": "ownerResourceID", "objectPropertyPath": "name" }, + { "pattern": ".*" } + ] } diff --git a/plugins/AutoTask/v1/dataStreams/contractStatus.json b/plugins/AutoTask/v1/dataStreams/contractStatus.json index bfe1995..c652a39 100644 --- a/plugins/AutoTask/v1/dataStreams/contractStatus.json +++ b/plugins/AutoTask/v1/dataStreams/contractStatus.json @@ -25,7 +25,7 @@ "metadata": [ { "name": "sourceId", "displayName": "Object ID", "shape": "string", "visible": false }, { "name": "id", "displayName": "ID", "sourceType": "autotask-contract", "shape": "string", "visible": false }, - { "name": "contractName", "displayName": "Contract Name", "sourceId": "id", "objectPropertyPath": "name" }, + { "name": "contractName", "displayName": "Contract Name" }, { "name": "companyID", "displayName": "Company ID", "sourceType": "autotask-company", "shape": "string", "visible": false }, { "name": "companyName", "displayName": "Company", "sourceId": "companyID", "objectPropertyPath": "name" }, { diff --git a/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json b/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json index 9147f5a..c193222 100644 --- a/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json +++ b/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json @@ -1,6 +1,6 @@ { "name": "resourceUtilisation", - "displayName": "Resource Utilisation", + "displayName": "Resource Utilization", "description": "Time entries logged by resources within the selected timeframe", "tags": ["Resources"], "baseDataSourceName": "httpRequestUnscoped", diff --git a/plugins/AutoTask/v1/dataStreams/tickets.json b/plugins/AutoTask/v1/dataStreams/tickets.json index 6d017b9..b62e28a 100644 --- a/plugins/AutoTask/v1/dataStreams/tickets.json +++ b/plugins/AutoTask/v1/dataStreams/tickets.json @@ -3,12 +3,15 @@ "displayName": "Tickets", "description": "Tickets with last activity within the selected timeframe", "tags": ["Tickets"], - "baseDataSourceName": "httpRequestUnscoped", + "baseDataSourceName": "httpRequestScopedSingle", "timeframes": true, + "matches": { + "sourceType": "autotask-company" + }, "config": { "httpMethod": "get", "expandInnerObjects": true, - "endpointPath": "atservicesrest/v1.0/Tickets/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"gte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.start}}\"},{\"op\":\"lte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.end}}\"}]}", + "endpointPath": "atservicesrest/v1.0/Tickets/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"eq\",\"field\":\"companyID\",\"value\":{{objects[0].companyId}}},{\"op\":\"in\",\"field\":\"status\",\"value\":[{{statusFilter}}]},{\"op\":\"in\",\"field\":\"priority\",\"value\":[{{priorityFilter}}]},{\"op\":\"gte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.start}}\"},{\"op\":\"lte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.end}}\"}]}", "pathToData": "items", "paging": { "mode": "nextUrl", @@ -20,7 +23,8 @@ } }, "metadata": [ - { "name": "id", "displayName": "Ticket ID", "shape": "string" }, + { "name": "sourceId", "displayName": "Object ID", "shape": "string", "visible": false }, + { "name": "id", "displayName": "Ticket ID", "shape": "string", "visible": false }, { "name": "title", "displayName": "Title" }, { "name": "description", "displayName": "Description", "visible": false }, { @@ -38,7 +42,13 @@ } }] }, - { "name": "priority", "displayName": "Priority", "shape": ["number", { "decimalPlaces": 0 }] }, + { + "name": "priorityName", + "displayName": "Priority", + "computed": true, + "valueExpression": "{{ $['priority'] == 1 ? 'Critical' : $['priority'] == 2 ? 'High' : $['priority'] == 3 ? 'Medium' : $['priority'] == 4 ? 'Low' : 'Unknown' }}" + }, + { "name": "priority", "displayName": "Priority (Raw)", "shape": ["number", { "decimalPlaces": 0 }], "visible": false }, { "name": "companyID", "sourceType": "autotask-company", "shape": "string", "visible": false }, { "name": "companyName", "displayName": "Company", "sourceId": "companyID", "objectPropertyPath": "name" }, { "name": "contactID", "sourceType": "autotask-contact", "shape": "string", "visible": false }, @@ -53,10 +63,114 @@ { "name": "firstResponseAssignedResourceName", "displayName": "First Response Resource", "sourceId": "firstresponeassignedresourceID", "objectPropertyPath": "name" }, { "name": "LastActivityResourceID", "sourceType": "autotask-resource", "shape": "string", "visible": false }, { "name": "lastActivityResourceName", "displayName": "Last Activity Resource", "sourceId": "LastActivityResourceID", "objectPropertyPath": "name" }, + { "name": "issueType", "displayName": "Issue Type" }, + { "name": "resolution", "displayName": "Resolution" }, { "name": "CreatorResourceID", "sourceType": "autotask-resource", "shape": "string", "visible": false }, { "name": "creatorResourceName", "displayName": "Creator", "sourceId": "CreatorResourceID", "objectPropertyPath": "name" }, { "name": "lastActivityDate", "displayName": "Last Activity Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, { "name": "dueDateTime", "displayName": "Due Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, - { "pattern": ".*", "visible": false } + { "name": "apiVendorID", "displayName": "API Vendor ID", "shape": "string", "visible": false }, + { "name": "assignedResourceRoleID", "displayName": "Assigned Resource Role ID", "shape": "string", "visible": false }, + { "name": "billingCodeID", "displayName": "Billing Code ID", "shape": "string", "visible": false }, + { "name": "companyLocationID", "displayName": "Company Location ID", "shape": "string", "visible": false }, + { "name": "configurationItemID", "displayName": "Configuration Item ID", "shape": "string", "visible": false }, + { "name": "contractServiceBundleID", "displayName": "Contract Service Bundle ID", "shape": "string", "visible": false }, + { "name": "contractServiceID", "displayName": "Contract Service ID", "shape": "string", "visible": false }, + { "name": "createdByContactID", "displayName": "Created By Contact ID", "shape": "string", "visible": false }, + { "name": "externalID", "displayName": "External ID", "shape": "string", "visible": false }, + { "name": "firstResponseInitiatingResourceID", "displayName": "First Response Initiating Resource ID", "shape": "string", "visible": false }, + { "name": "impersonatorCreatorResourceID", "displayName": "Impersonator Creator Resource ID", "shape": "string", "visible": false }, + { "name": "monitorID", "displayName": "Monitor ID", "shape": "string", "visible": false }, + { "name": "monitorTypeID", "displayName": "Monitor Type ID", "shape": "string", "visible": false }, + { "name": "opportunityID", "displayName": "Opportunity ID", "shape": "string", "visible": false }, + { "name": "organizationalLevelAssociationID", "displayName": "Organizational Level Association ID", "shape": "string", "visible": false }, + { "name": "problemTicketId", "displayName": "Problem Ticket ID", "shape": "string", "visible": false }, + { "name": "projectID", "displayName": "Project ID", "shape": "string", "visible": false }, + { "name": "queueID", "displayName": "Queue ID", "shape": "string", "visible": false }, + { "name": "rmmAlertID", "displayName": "RMM Alert ID", "shape": "string", "visible": false }, + { "name": "serviceLevelAgreementID", "displayName": "SLA ID", "shape": "string", "visible": false }, + { "name": "completedDate", "displayName": "Completed Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "createDate", "displayName": "Created Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "firstResponseDateTime", "displayName": "First Response Date Time", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "firstResponseDueDateTime", "displayName": "First Response Due Date Time", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "lastCustomerNotificationDateTime", "displayName": "Last Customer Notification Date Time", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "lastCustomerVisibleActivityDateTime", "displayName": "Last Customer Visible Activity Date Time", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "lastTrackedModificationDateTime", "displayName": "Last Tracked Modification Date Time", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "resolutionPlanDateTime", "displayName": "Resolution Plan Date Time", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "resolutionPlanDueDateTime", "displayName": "Resolution Plan Due Date Time", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "resolvedDateTime", "displayName": "Resolved Date Time", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "resolvedDueDateTime", "displayName": "Resolved Due Date Time", "shape": ["date", { "timeZone": "Etc/UTC" }] }, + { "name": "estimatedHours", "displayName": "Estimated Hours", "shape": ["number", { "decimalPlaces": 2 }] }, + { "name": "hoursToBeScheduled", "displayName": "Hours To Be Scheduled", "shape": ["number", { "decimalPlaces": 2 }] }, + { "name": "serviceLevelAgreementPausedNextEventHours", "displayName": "SLA Paused Next Event Hours", "shape": ["number", { "decimalPlaces": 2 }] }, + { "name": "changeApprovalBoard", "displayName": "Change Approval Board" , "visible": false }, + { "name": "changeApprovalStatus", "displayName": "Change Approval Status" , "visible": false }, + { "name": "changeApprovalType", "displayName": "Change Approval Type" , "visible": false }, + { "name": "changeInfoField1", "displayName": "Change Info Field 1", "visible": false }, + { "name": "changeInfoField2", "displayName": "Change Info Field 2", "visible": false }, + { "name": "changeInfoField3", "displayName": "Change Info Field 3", "visible": false }, + { "name": "changeInfoField4", "displayName": "Change Info Field 4", "visible": false }, + { "name": "changeInfoField5", "displayName": "Change Info Field 5", "visible": false }, + { "name": "creatorType", "displayName": "Creator Type" }, + { "name": "currentServiceThermometerRating", "displayName": "Current Service Thermometer Rating" }, + { "name": "isAssignedToComanaged", "displayName": "Is Assigned To Co-Managed", "visible": false }, + { "name": "isVisibleToComanaged", "displayName": "Is Visible To Co-Managed", "visible": false }, + { "name": "lastActivityPersonType", "displayName": "Last Activity Person Type", "visible": false }, + { "name": "previousServiceThermometerRating", "displayName": "Previous Service Thermometer Rating", "visible": false }, + { "name": "purchaseOrderNumber", "displayName": "Purchase Order Number" }, + { "name": "rmaStatus", "displayName": "RMA Status" }, + { "name": "rmaType", "displayName": "RMA Type" }, + { "name": "serviceLevelAgreementHasBeenMet", "displayName": "SLA Has Been Met" }, + { "name": "serviceThermometerTemperature", "displayName": "Service Thermometer Temperature", "visible": false }, + { "name": "source", "displayName": "Source" , "visible": false}, + { "name": "subIssueType", "displayName": "Sub Issue Type", "visible": false }, + { "name": "ticketCategory", "displayName": "Ticket Category" }, + { "name": "ticketNumber", "displayName": "Ticket Number" }, + { "name": "ticketType", "displayName": "Ticket Type" }, + { "pattern": ".*" , "visible": false } + ], + "ui": [ + { + "name": "statusFilter", + "type": "autocomplete", + "label": "Status", + "allowCustomValues": false, + "isMulti": false, + "defaultValue": "1,5,8,9,10,11,12,13", + "data": { + "source": "fixed", + "values": [ + { "value": "1,5,8,9,10,11,12,13", "label": "All Statuses" }, + { "value": "1", "label": "New" }, + { "value": "8", "label": "In Progress" }, + { "value": "9", "label": "Waiting Customer" }, + { "value": "10", "label": "Waiting Materials" }, + { "value": "11", "label": "Waiting Vendor" }, + { "value": "13", "label": "Waiting Approval" }, + { "value": "12", "label": "Escalate" }, + { "value": "5", "label": "Complete" } + ] + }, + "validation": { "required": true } + }, + { + "name": "priorityFilter", + "type": "autocomplete", + "label": "Priority", + "allowCustomValues": false, + "isMulti": false, + "defaultValue": "1,2,3,4", + "data": { + "source": "fixed", + "values": [ + { "value": "1,2,3,4", "label": "All Priorities" }, + { "value": "1", "label": "Critical" }, + { "value": "2", "label": "High" }, + { "value": "3", "label": "Medium" }, + { "value": "4", "label": "Low" } + ] + }, + "validation": { "required": true } + } ] } diff --git a/plugins/AutoTask/v1/metadata.json b/plugins/AutoTask/v1/metadata.json index 80d1f12..460b297 100644 --- a/plugins/AutoTask/v1/metadata.json +++ b/plugins/AutoTask/v1/metadata.json @@ -6,7 +6,7 @@ "name": "@TimWheeler-SQUP", "type": "community" }, - "description": "AutoTask - Dashboard Companies, Tickets, Resources and Contacts", + "description": "Monitor tickets, contracts, projects, resources, and company data from AutoTask PSA", "category": "User Defined", "type": "cloud", "schemaVersion": "2.0", diff --git a/test-present.txt b/test-present.txt new file mode 100644 index 0000000..30d74d2 --- /dev/null +++ b/test-present.txt @@ -0,0 +1 @@ +test \ No newline at end of file From 03253102dbac29e2c8125789ef0ee9cbf6c55448 Mon Sep 17 00:00:00 2001 From: Tim Wheeler <63284593+TimWheeler-SQUP@users.noreply.github.com> Date: Sat, 9 May 2026 21:07:32 +0100 Subject: [PATCH 19/21] fix OOB dashboards and reduce API calls for high number of objects --- .../v1/dataStreams/resourceUtilisation.json | 2 +- .../v1/dataStreams/scripts/tickets.js | 2 + .../v1/dataStreams/surveyResults.json | 2 +- plugins/AutoTask/v1/dataStreams/tickets.json | 13 ++-- .../v1/defaultContent/tickets.dash.json | 70 ++++++------------- 5 files changed, 34 insertions(+), 55 deletions(-) create mode 100644 plugins/AutoTask/v1/dataStreams/scripts/tickets.js diff --git a/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json b/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json index c193222..423db20 100644 --- a/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json +++ b/plugins/AutoTask/v1/dataStreams/resourceUtilisation.json @@ -4,7 +4,7 @@ "description": "Time entries logged by resources within the selected timeframe", "tags": ["Resources"], "baseDataSourceName": "httpRequestUnscoped", - "timeframes": ["last7days", "last30days", "thisMonth", "lastMonth", "thisQuarter", "lastQuarter", "thisYear"], + "timeframes": true, "config": { "httpMethod": "get", "expandInnerObjects": true, diff --git a/plugins/AutoTask/v1/dataStreams/scripts/tickets.js b/plugins/AutoTask/v1/dataStreams/scripts/tickets.js new file mode 100644 index 0000000..783cbb8 --- /dev/null +++ b/plugins/AutoTask/v1/dataStreams/scripts/tickets.js @@ -0,0 +1,2 @@ +const selectedIds = new Set(context.objects.map(o => String(o.companyId))); +result = (data.items ?? []).filter(ticket => selectedIds.has(String(ticket.companyID))); diff --git a/plugins/AutoTask/v1/dataStreams/surveyResults.json b/plugins/AutoTask/v1/dataStreams/surveyResults.json index 80348b5..16aa655 100644 --- a/plugins/AutoTask/v1/dataStreams/surveyResults.json +++ b/plugins/AutoTask/v1/dataStreams/surveyResults.json @@ -4,7 +4,7 @@ "description": "Survey results submitted within the selected timeframe", "tags": ["Surveys"], "baseDataSourceName": "httpRequestUnscoped", - "timeframes": ["last24hours", "last7days", "last30days", "thisMonth", "lastMonth", "thisQuarter", "lastQuarter", "thisYear"], + "timeframes": true, "config": { "httpMethod": "get", "expandInnerObjects": true, diff --git a/plugins/AutoTask/v1/dataStreams/tickets.json b/plugins/AutoTask/v1/dataStreams/tickets.json index b62e28a..cd4e35a 100644 --- a/plugins/AutoTask/v1/dataStreams/tickets.json +++ b/plugins/AutoTask/v1/dataStreams/tickets.json @@ -3,7 +3,7 @@ "displayName": "Tickets", "description": "Tickets with last activity within the selected timeframe", "tags": ["Tickets"], - "baseDataSourceName": "httpRequestScopedSingle", + "baseDataSourceName": "httpRequestScoped", "timeframes": true, "matches": { "sourceType": "autotask-company" @@ -11,7 +11,8 @@ "config": { "httpMethod": "get", "expandInnerObjects": true, - "endpointPath": "atservicesrest/v1.0/Tickets/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"eq\",\"field\":\"companyID\",\"value\":{{objects[0].companyId}}},{\"op\":\"in\",\"field\":\"status\",\"value\":[{{statusFilter}}]},{\"op\":\"in\",\"field\":\"priority\",\"value\":[{{priorityFilter}}]},{\"op\":\"gte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.start}}\"},{\"op\":\"lte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.end}}\"}]}", + "endpointPath": "atservicesrest/v1.0/Tickets/query?search={\"MaxRecords\":500,\"filter\":[{\"op\":\"in\",\"field\":\"status\",\"value\":[{{statusFilter}}]},{\"op\":\"in\",\"field\":\"priority\",\"value\":[{{priorityFilter}}]},{\"op\":\"gte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.start}}\"},{\"op\":\"lte\",\"field\":\"lastActivityDate\",\"value\":\"{{timeframe.end}}\"}]}", + "postRequestScript": "tickets.js", "pathToData": "items", "paging": { "mode": "nextUrl", @@ -134,8 +135,8 @@ "name": "statusFilter", "type": "autocomplete", "label": "Status", - "allowCustomValues": false, - "isMulti": false, + "allowCustomValues": true, + "isMulti": true, "defaultValue": "1,5,8,9,10,11,12,13", "data": { "source": "fixed", @@ -157,8 +158,8 @@ "name": "priorityFilter", "type": "autocomplete", "label": "Priority", - "allowCustomValues": false, - "isMulti": false, + "allowCustomValues": true, + "isMulti": true, "defaultValue": "1,2,3,4", "data": { "source": "fixed", diff --git a/plugins/AutoTask/v1/defaultContent/tickets.dash.json b/plugins/AutoTask/v1/defaultContent/tickets.dash.json index 2cd934a..9310533 100644 --- a/plugins/AutoTask/v1/defaultContent/tickets.dash.json +++ b/plugins/AutoTask/v1/defaultContent/tickets.dash.json @@ -283,14 +283,18 @@ "i": "73f1150f-0a49-49be-974f-f2da0e33a0b5", "z": 0, "config": { - "timeframe": "last30days", + "timeframe": "last7days", "dataStream": { "name": "tickets", + "dataSourceConfig": { + "statusFilter": "1,5,8,9,10,11,12,13", + "priorityFilter": "1,2,3,4" + }, "filter": { "multiOperation": "and", "filters": [ { - "column": "resolvedDateTime", + "column": "completedDate", "operation": "empty" } ] @@ -311,7 +315,7 @@ } }, "scope": { - "query": "g.V().order().by('__name').hasNot('__canonicalType').has(\"__configId\", within(\"{{configId}}\")).limit(500)", + "query": "g.V().order().by('__name').hasNot('__canonicalType').has(\"__configId\", within(\"{{configId}}\")).or(__.has(\"sourceType\", \"autotask-company\")).limit(500)", "bindings": {}, "queryDetail": {} }, @@ -322,29 +326,13 @@ ], "title": "Tickets per Assigned Resource", "visualisation": { - "type": "data-stream-bar-chart", + "type": "data-stream-table", "config": { - "data-stream-bar-chart": { - "xAxisGroup": "none", - "showLegend": false, - "range": { - "type": "auto" - }, - "showGrid": true, - "grouping": false, - "displayMode": "actual", - "xAxisData": "assignedResourceName_uniqueValues", - "showTotals": false, - "yAxisLabel": "", - "horizontalLayout": "vertical", - "showValue": false, - "yAxisData": [ + "data-stream-table": { + "columnOrder": [ + "assignedResourceName_uniqueValues", "count" - ], - "showYAxisLabel": true, - "xAxisLabel": "", - "legendPosition": "bottom", - "showXAxisLabel": true + ] } } } @@ -363,11 +351,15 @@ "timeframe": "last7days", "dataStream": { "name": "tickets", + "dataSourceConfig": { + "statusFilter": "1,5,8,9,10,11,12,13", + "priorityFilter": "1,2,3,4" + }, "filter": { "multiOperation": "and", "filters": [ { - "column": "resolvedDateTime", + "column": "completedDate", "operation": "notempty" } ] @@ -388,7 +380,7 @@ } }, "scope": { - "query": "g.V().order().by('__name').hasNot('__canonicalType').has(\"__configId\", within(\"{{configId}}\")).limit(500)", + "query": "g.V().order().by('__name').hasNot('__canonicalType').has(\"__configId\", within(\"{{configId}}\")).or(__.has(\"sourceType\", \"autotask-company\")).limit(500)", "bindings": {}, "queryDetail": {} }, @@ -399,29 +391,13 @@ ], "title": "Tickets Closed per Resource", "visualisation": { - "type": "data-stream-bar-chart", + "type": "data-stream-table", "config": { - "data-stream-bar-chart": { - "xAxisGroup": "none", - "showLegend": false, - "range": { - "type": "auto" - }, - "showGrid": true, - "grouping": false, - "displayMode": "actual", - "xAxisData": "assignedResourceName_uniqueValues", - "showTotals": false, - "yAxisLabel": "", - "horizontalLayout": "vertical", - "showValue": false, - "yAxisData": [ + "data-stream-table": { + "columnOrder": [ + "assignedResourceName_uniqueValues", "count" - ], - "showYAxisLabel": true, - "xAxisLabel": "", - "legendPosition": "bottom", - "showXAxisLabel": true + ] } } } From ce3ec2a03dce96979d8632e7d06fb62f7e0d6c42 Mon Sep 17 00:00:00 2001 From: Tim Wheeler <63284593+TimWheeler-SQUP@users.noreply.github.com> Date: Sat, 9 May 2026 21:44:18 +0100 Subject: [PATCH 20/21] Fix oob dashboard --- .../v1/defaultContent/tickets.dash.json | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/plugins/AutoTask/v1/defaultContent/tickets.dash.json b/plugins/AutoTask/v1/defaultContent/tickets.dash.json index 9310533..26f271a 100644 --- a/plugins/AutoTask/v1/defaultContent/tickets.dash.json +++ b/plugins/AutoTask/v1/defaultContent/tickets.dash.json @@ -46,10 +46,8 @@ "monitor": { "tileRollsUp": true, "monitorType": "threshold", - "aggregation": "count", - "groupBy": "__group_by_none__", - "condition": { - "columns": [], + "condition": { + "columns": [], "logic": { "if": [ { @@ -65,6 +63,8 @@ } }, "_type": "simple", + "aggregation": "count", + "groupBy": "__group_by_none__", "frequency": 720 }, "activePluginConfigIds": [ @@ -287,8 +287,8 @@ "dataStream": { "name": "tickets", "dataSourceConfig": { - "statusFilter": "1,5,8,9,10,11,12,13", - "priorityFilter": "1,2,3,4" + "priorityFilter": "1,2,3,4", + "statusFilter": "1,5,8,9,10,11,12,13" }, "filter": { "multiOperation": "and", @@ -332,7 +332,8 @@ "columnOrder": [ "assignedResourceName_uniqueValues", "count" - ] + ], + "hiddenColumns": [] } } } @@ -352,8 +353,8 @@ "dataStream": { "name": "tickets", "dataSourceConfig": { - "statusFilter": "1,5,8,9,10,11,12,13", - "priorityFilter": "1,2,3,4" + "priorityFilter": "1,2,3,4", + "statusFilter": "1,5,8,9,10,11,12,13" }, "filter": { "multiOperation": "and", @@ -397,14 +398,15 @@ "columnOrder": [ "assignedResourceName_uniqueValues", "count" - ] + ], + "hiddenColumns": [] } } } } } ], - "version": 78, + "version": 83, "columns": 4 }, "path": "tickets", From b73c42235bb3322dd3cf6c42e1b5d0db6ef18915 Mon Sep 17 00:00:00 2001 From: Tim Wheeler <63284593+TimWheeler-SQUP@users.noreply.github.com> Date: Sun, 10 May 2026 20:12:44 +0100 Subject: [PATCH 21/21] Apply review feedback and remove non-plugin files from PR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename displayName AutoTask → Autotask, update name to autotask - Update description to reference Datto Autotask PSA with full stop - Add documentation link to metadata.json; bump version to 1.0.1 - Change category from User Defined to Service Management - Update configValidation, custom_types, ui.json, README: AutoTask → Autotask - Fix ui.json help text to start with verbs and drop possessive phrasing - Add computed statusName with state mapping to projectStatus.json - Add plugins/AutoTask to CODEOWNERS - Remove CLAUDE.md, CLAUDE.local.md, and test-present.txt from PR Co-Authored-By: Claude Sonnet 4.6 --- .github/CODEOWNERS | 1 + CLAUDE.local.md | 415 ------------------ CLAUDE.md | 23 - plugins/AutoTask/v1/configValidation.json | 6 +- plugins/AutoTask/v1/custom_types.json | 10 +- .../v1/dataStreams/projectStatus.json | 15 +- plugins/AutoTask/v1/dataStreams/tickets.json | 6 +- plugins/AutoTask/v1/docs/README.md | 10 +- plugins/AutoTask/v1/metadata.json | 17 +- plugins/AutoTask/v1/ui.json | 8 +- test-present.txt | 1 - 11 files changed, 46 insertions(+), 466 deletions(-) delete mode 100644 CLAUDE.local.md delete mode 100644 CLAUDE.md delete mode 100644 test-present.txt diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9948a76..2fa7725 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,6 +4,7 @@ /.github/* @squaredup/community-moderators # Request review from original author +plugins/AutoTask/* @TimWheeler-SQUP plugins/DattoRMM/* @TimWheeler-SQUP plugins/DigiCert/* @shaswot77 plugins/FantasyPremierLeague/* @TimWheeler-SQUP diff --git a/CLAUDE.local.md b/CLAUDE.local.md deleted file mode 100644 index b6ab359..0000000 --- a/CLAUDE.local.md +++ /dev/null @@ -1,415 +0,0 @@ -# SquaredUp Plugin Authoring — Local Reference - -## What is a SquaredUp plugin -In this repo all plugins are based on the low-code plugin (LCP) framework. -This can be seen in the repo squaredup-plugin-repository under \squaredup-plugin-repository\plugins\WebAPI\v1 - -A plugin is a light weight definition of how to connect, import (or index) and display data from a 3rd party application via API into SquaredUp - -LCP plugins has it's own Confluence space - https://squaredup-eng.atlassian.net/wiki/spaces/LCP/overview - -Out-of-the-box (OOTB) dashboards can be bundled with your plugin so users get useful content immediately on installation. - -Dashboard files live in the defaultContent/ folder and follow the same format as standard SquaredUp dashboards. The captureOobContent script works the same way as for regular plugins. - -When exporting your plugin, you can optionally select dashboards to include on the Dashboards tab of the export modal. If you do, make sure those dashboards only use data streams that are included in the same export. - -## UI schema -The UI schema is used in two places in an LCP: -ui.json — the setup form shown when installing (adding) the plugin. Typically collects credentials. -ui array in data stream files — the form shown on the Parameters step when configuring a tile. - -Both use the same field definition format. - -Note: Do not set a title attribute on fields. It is not used and should be omitted. - -Example — a password field for plugin setup - -[ - { - "type": "password", - "name": "apiKey", - "label": "API key", - "help": "Create an API key in the [provider portal](https://example.com/api-keys)", - "validation": { - "required": true - }, - "placeholder": "e.g. sk_live_xxxxxxxxxxxxxxxx" - } -] -Example — a text field for data stream config - - - -[ - { - "name": "domain", - "type": "text", - "label": "Domain", - "placeholder": "mydomain.com", - "validation": { - "required": true - } - } -] -Using field values in config - -Reference field values in your config block using {{fieldName}} (Mustache syntax): - - - -{ - "baseUrl": "https://api.example.com/{{apiKey}}", - "endpointPath": "domain/{{domain}}" -} - -### Accessing plugin setup (datasource) config in a data stream - -Fields defined in `ui.json` (the plugin setup form) are accessible in data stream `endpointPath` and other config using the `datasource.` prefix: - -```json -"endpointPath": "entry/{{dataSource.userID}}" -``` - -Fields defined in a data stream's own `ui` array are referenced without the prefix: - -```json -"endpointPath": "entry/{{userID}}" -``` - -Use `{{dataSource.fieldName}}` when the value comes from plugin installation (e.g. an account ID set once for all tiles). Use `{{fieldName}}` when the value is provided per tile. In a UI section in the same .json file. - -## Terminology - -Plugin - The packaged integration (e.g. "My GitHub Plugin") - -Data source - An installed instance of a plugin, configured with credentials - -Data stream - A named query against a data source — typically maps to a single API endpoint - -Base plugin - The underlying plugin your LCP is built on (almost always Web API, but could be PowerShell or something else) - -Object indexing / import - Importing objects from your integration into the SquaredUp (knowledge) graph, provides global search and drilldown capabilities - -OOB dashboards / default content - Out-of-the-box dashboards bundled with your plugin - -## Plugin import (in-app name = indexing) -The import creates objects in a database graph for use in SquaredUp. Data streams can reference these objects. - -This is the structure of the JSON files found in the indexDefinitions folder of a low-code plugin. - -{ - "name": "vehicles", - "dataStream": { - "name": "vehicles", - "config": {} // used as dataSourceConfig - optional - }, - "timeframe": "none", - "objectMapping": { - "id": "name", - "name": "name", - // literal strings are always column names, use properties for values - "type": { "value": "starwars-vehicle" }, - "properties": [ - // add a column as an object property - "model", - "manufacturer", - "vehicle_class", - // add a column as a property with a different name - { "crewCount": "crew" }, - // convention is one property per object, but multiple also works - { "passengerCount": "passengers" }, - { "maxSpeed": "max_atmosphering_speed" }, - // add a property with a fixed value - { "owner": { "value": "Disney" } } - ] - } -} - - -## Plugins UX guidelines -Use this page when asked to review the format or naming conventions within the plugin -https://squaredup-eng.atlassian.net/wiki/spaces/SC/pages/26923138285589/Plugins+UX+Guidelines - -### Metadata.json -Every plugin has a metadata.json file at its root. This describes the plugin and controls how it appears in the catalog. - -The core fields (name, displayName, description, category, author, and icon) are set when you export from the UI. The remaining fields — notably links, keywords, and any adjustments to base.config — must be added or edited manually in the JSON after export. - -Example - - - -{ - "name": "my-plugin", - "displayName": "My Plugin", - "version": "1.0.0", - "schemaVersion": "2.0", - "category": "SquaredUp Internal", - "type": "cloud", - "author": { - "type": "community", - "name": "your-github-username" - }, - "base": { - "plugin": "WebAPI", - "majorVersion": "1", - "config": {} - }, - "links": [ - { "category": "documentation", "url": "https://github.com/squaredup/plugins/blob/main/plugins/MyPlugin/v1/docs/README.md", "label": "Help adding this plugin" }, - { "category": "source", "url": "https://github.com/squaredup/plugins/tree/main/plugins/MyPlugin/v1", "label": "Repository" } - ], - "keywords": ["monitoring", "alerts", "myservice"] -} - -### Data streams -A data stream typically maps to a single API endpoint. It defines what request to make, how to authenticate, how to present the response, and optionally how to let users configure the query at tile creation time. - -Each data stream lives in its own JSON file inside the dataStreams/ folder of your plugin. - -Basic structure - -{ - "name": "person", - "displayName": "Person", - "description": "Details for a single person", - "tags": ["people"], - "baseDataSourceName": "httpRequestUnscoped", - "config": { - "httpMethod": "get", - "endpointPath": "/people/{{personId}}", - "postRequestScript": "person-post.js" - }, - "ui": [ - { - "label": "Person ID", - "name": "personId", - "type": "text" - } - ] -} -Other properties like timeframes and metadata can also be included; defaults are inherited from the base plugin. - -Pattern-based hiding (e.g. UDF fields) -```json -{ "pattern": "udf.*", "visible": false } -``` -Place before the catch-all `{ "pattern": ".*" }`. This means that all fields not specifically listed in the data stream json file, will be mapped to columns. Without this only the defined columns are shown. - -## Base URL Pattern -- `metadata.json` `baseUrl` must be a plain `{{variable}}` — no path suffixes like `{{baseUrl}}/api/v2/` (the `/api/v2/` gets silently dropped) -- Put the full path prefix in each data stream's `endpointPath` instead: `"endpointPath": "api/v2/account/sites"` - -## Data Stream Config - -### baseDataSourceName values -| Value | Use | -|---|---| -| `httpRequestUnscoped` | No object picker — fetches globally | -| `httpRequestScoped` | Object picker — one request with all selected objects available as `{{objects}}` array | -| `httpRequestScopedSingle` | Object picker — framework automatically iterates, making one request per selected object | - -### Scoped data streams -- `matches.sourceType` constrains the object picker to a specific type -- Use `{{objects[0].propertyName}}` in `endpointPath` to reference selected object properties -- **Do not use `{{sourceId}}`** — SquaredUp's `sourceId` is prefixed with the type (e.g. `Datto Site/e63d2f98-...`), which breaks URL paths -- Use a custom property stored during import instead (e.g. `{{objects[0].siteUid}}`) -- Always include a hidden `sourceId` column in scoped data stream metadata (pattern from Pingdom): - ```json - { "name": "sourceId", "displayName": "Object ID", "shape": "string", "visible": false } - ``` - -### Paging in data streams - -Two paging mechanisms are used across plugins, depending on where the API returns the next page URL: - -**Next URL in response body** (e.g. Datto RMM — `pageDetails.nextPageUrl`): -```json -"paging": { - "mode": "nextUrl", - "pageSize": { "realm": { "value": "none", "label": "none" } }, - "in": { - "realm": { "value": "payload", "label": "payload" }, - "path": "pageDetails.nextPageUrl" - } -} -``` - -**Next URL in Link header** (e.g. GitHub — standard RFC 5988 `Link: ; rel="next"`): -```json -"paging": { - "mode": "nextUrl", - "pageSize": { - "path": "per_page", - "realm": { "value": "queryArg", "label": "Query parameter" }, - "value": "100" - }, - "in": { - "path": "next", - "realm": { "value": "webLink", "label": "Web link" } - } -} -``` - -## Metadata Shapes - -### Dates (unix timestamps in ms) -```json -{ "name": "creationDate", "shape": ["date", { "timeZone": "Etc/UTC" }] } -``` -Plain `"shape": "date"` also works for standard date strings. - -### Numbers -```json -{ "name": "count", "shape": ["number", { "decimalPlaces": 0 }] } -``` - -### State with value mapping -```json -{ - "name": "status", - "shape": ["state", { - "map": { - "success": ["Compliant", "up"], - "error": ["Not Compliant", "down"], - "warning": [], - "unknown": [], - "unmonitored": [] - } - }] -} -``` - -### Computed/derived column -```json -{ - "name": "derivedState", - "displayName": "Compliance State", - "computed": true, - "valueExpression": "{{ $['softwareStatus'] }}", - "shape": ["state", { "map": { "success": ["Compliant"], "error": ["Not Compliant"] } }] -} -``` - -### Hiding fields -```json -{ "name": "uid", "shape": "string", "visible": false } -``` - -## Known Issues / Gotchas -- `"providesPluginDiagnostics": true` fails schema validation on the squaredup-plugin-repository pipeline — avoid or remove before submitting a PR -- `"importNotSupported": false` is not a recognised `metadata.json` field and will also fail schema validation — remove it. Import is enabled by default whenever `indexDefinitions/` is present; no flag needed to enable it. -- `*.local.json` and `*.local.md` are gitignored in this repo - -## Schemas -When creating or editing a plugin file, fetch the relevant schema URL and validate your JSON against it. - -| File | Schema URL | -|---|---| -| `configValidation.json` | https://s3.us-east-1.amazonaws.com/plugins.squaredup.saas/schemas/latest/configValidation.schema.json | -| `dataStream.json` (LCP) | https://s3.us-east-1.amazonaws.com/plugins.squaredup.saas/schemas/latest/dataStream.schema.json | -| `defaultDashboards.json` | https://s3.us-east-1.amazonaws.com/plugins.squaredup.saas/schemas/latest/defaultDashboards.schema.json | -| `metadata.json` | https://s3.us-east-1.amazonaws.com/plugins.squaredup.saas/schemas/latest/metadata.schema.json | -| `datastream.json` (WebAPI) | https://s3.us-east-1.amazonaws.com/plugins.squaredup.saas/schemas/latest/webapi/datastream.schema.json | - - -## CLI Commands -suffix is used to allow the deployment to be a unique name - -```bash -squaredup status # check login state -squaredup login --region --stage master -squaredup deploy --suffix --stage master # dev only -squaredup deploy --suffix # eu / us -squaredup delete --stage master -``` -### Custom types -JSON only — custom_types.json cannot be configured via the SquaredUp UI. It must be created and edited directly in your plugin folder. - -Custom types control how your plugin's indexed objects are displayed across SquaredUp — their label, plural form, and the icon shown in global search, the graph explorer, and object drilldown pages. - -File location - -Create a custom_types.json file at the root of your plugin folder. It is an array with one entry per object type your plugin defines. - -Example - -[ - { - "name": "My Plugin Device", - "sourceType": "my-plugin-device", - "icon": "server", - "singular": "Device", - "plural": "Devices" - } -] -Properties - -Property - -Description - -name - -An internal label for this type. Convention is " " — e.g. "UniFi Network Device" - -sourceType - -Must match the type value used in your indexDefinitions/default.json objectMapping. This is how SquaredUp links the display config to actual indexed objects - -icon - -A Lucide icon name in lowercase kebab-case (e.g. server, wifi, hard-drive, bar-chart, key, camera) - -singular - -Singular display label shown in the UI — e.g. "Device" - -plural - -Plural display label shown in the UI — e.g. "Devices" - -Relationship to object indexing - -The sourceType here must match the type value assigned in your index definition. For example: - -// indexDefinitions/default.json -"objectMapping": { - "type": { "value": "my-plugin-device" }, - ... -} - -// custom_types.json -{ "sourceType": "my-plugin-device", ... } -Without a matching entry in custom_types.json, indexed objects still appear in SquaredUp — they'll just use a generic icon and the raw sourceType string as their display label. - -Choosing icons - -Browse lucide.dev/icons to find an appropriate icon. Use the icon name in lowercase kebab-case (e.g. hard-drive, not HardDrive). - -Multiple types - -A plugin can define multiple types in the same file — one object per type: - -[ - { "name": "My Plugin Server", "sourceType": "myPlugin-server", "icon": "server", "singular": "Server", "plural": "Servers" }, - { "name": "My Plugin Database", "sourceType": "myPlugin-database", "icon": "database", "singular": "Database", "plural": "Databases" } -] -Examples - -UniFi custom_types.json — three types with different icons - -FantasyPremierLeague custom_types.json - -### Expressions -Expressions -In many areas of plugins, mustache-style expressions can be used for advanced configuration scenarios, e.g. - -Custom columns ({{ $['value'] > 99.9 ? 'success' : 'error' }}) - -Custom formatting ({{ $['value'] / 100 }}) - -Referencing objects or timeframe in Parameters ({{timeframe.start/end}}) - -Mapping UI fields to Parameters ({{myFieldName}}) \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 8ff872c..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,23 +0,0 @@ -# Plugin Authoring Guidelines - -## Status mappings - -Where an API returns a numeric or coded status value that needs mapping to a human-readable label and a SquaredUp state, implement the mapping directly in the data stream `.json` file using a `computed` column with a `valueExpression` ternary chain. Only use a `.js` post-request script when the mapping logic is too complex for an expression (e.g. requires lookups across multiple fields or external data). - -Example: - -```json -{ - "name": "statusName", - "displayName": "Status", - "computed": true, - "valueExpression": "{{ $['status'] == 1 ? 'Open' : $['status'] == 2 ? 'Closed' : 'Unknown' }}", - "shape": ["state", { - "map": { - "warning": ["Open"], - "success": ["Closed"], - "unknown": ["Unknown"] - } - }] -} -``` diff --git a/plugins/AutoTask/v1/configValidation.json b/plugins/AutoTask/v1/configValidation.json index 3212a51..8d8f70c 100644 --- a/plugins/AutoTask/v1/configValidation.json +++ b/plugins/AutoTask/v1/configValidation.json @@ -1,10 +1,10 @@ { "steps": [ { - "displayName": "AutoTask connection", + "displayName": "Autotask connection", "dataStream": { "name": "companies", "timeframe": "none" }, - "success": "Successfully connected to AutoTask", - "error": "Cannot connect to AutoTask — check your Zone URL, Integration Code, Username and Secret", + "success": "Successfully connected to Autotask", + "error": "Cannot connect to Autotask — check your Zone URL, Integration Code, Username and Secret", "required": true } ] diff --git a/plugins/AutoTask/v1/custom_types.json b/plugins/AutoTask/v1/custom_types.json index 24c6635..0aba596 100644 --- a/plugins/AutoTask/v1/custom_types.json +++ b/plugins/AutoTask/v1/custom_types.json @@ -1,34 +1,34 @@ [ { - "name": "AutoTask Company", + "name": "Autotask Company", "sourceType": "autotask-company", "icon": "building-2", "singular": "Company", "plural": "Companies" }, { - "name": "AutoTask Contact", + "name": "AutotaskContact", "sourceType": "autotask-contact", "icon": "user", "singular": "Contact", "plural": "Contacts" }, { - "name": "AutoTask Project", + "name": "AutotaskProject", "sourceType": "autotask-project", "icon": "folder-kanban", "singular": "Project", "plural": "Projects" }, { - "name": "AutoTask Resource", + "name": "AutotaskResource", "sourceType": "autotask-resource", "icon": "hard-hat", "singular": "Resource", "plural": "Resources" }, { - "name": "AutoTask Contract", + "name": "AutotaskContract", "sourceType": "autotask-contract", "icon": "file-text", "singular": "Contract", diff --git a/plugins/AutoTask/v1/dataStreams/projectStatus.json b/plugins/AutoTask/v1/dataStreams/projectStatus.json index 81d8d47..78a2139 100644 --- a/plugins/AutoTask/v1/dataStreams/projectStatus.json +++ b/plugins/AutoTask/v1/dataStreams/projectStatus.json @@ -22,7 +22,20 @@ "metadata": [ { "name": "id", "displayName": "ID", "shape": "string" }, { "name": "projectName", "displayName": "Project Name" }, - { "name": "status", "displayName": "Status", "shape": ["number", { "decimalPlaces": 0 }] }, + { "name": "status", "displayName": "Status (Raw)", "shape": ["number", { "decimalPlaces": 0 }], "visible": false }, + { + "name": "statusName", + "displayName": "Status", + "computed": true, + "valueExpression": "{{ $['status'] == 1 ? 'Active' : $['status'] == 2 ? 'Inactive' : $['status'] == 3 ? 'Completed' : $['status'] == 5 ? 'On Hold' : 'Unknown' }}", + "shape": ["state", { + "map": { + "success": ["Active", "Completed"], + "warning": ["On Hold"], + "unknown": ["Inactive", "Unknown"] + } + }] + }, { "name": "completedPercentage", "displayName": "Completed %", "shape": ["number", { "decimalPlaces": 0 }] }, { "name": "actualHours", "displayName": "Actual Hours", "shape": ["number", { "decimalPlaces": 2 }] }, { "name": "estimatedTime", "displayName": "Estimated Hours", "shape": ["number", { "decimalPlaces": 2 }] }, diff --git a/plugins/AutoTask/v1/dataStreams/tickets.json b/plugins/AutoTask/v1/dataStreams/tickets.json index cd4e35a..17766c6 100644 --- a/plugins/AutoTask/v1/dataStreams/tickets.json +++ b/plugins/AutoTask/v1/dataStreams/tickets.json @@ -60,13 +60,13 @@ { "name": "assignedResourceName", "displayName": "Assigned Resource", "sourceId": "assignedResourceID", "objectPropertyPath": "name" }, { "name": "completedByResourceID", "sourceType": "autotask-resource", "shape": "string", "visible": false }, { "name": "completedByResourceName", "displayName": "Completed By", "sourceId": "completedByResourceID", "objectPropertyPath": "name" }, - { "name": "firstresponeassignedresourceID", "sourceType": "autotask-resource", "shape": "string", "visible": false }, + { "name": "firstresponeassignedresourceID", "displayName": "First Response Assigned Resource ID", "sourceType": "autotask-resource", "shape": "string", "visible": false }, { "name": "firstResponseAssignedResourceName", "displayName": "First Response Resource", "sourceId": "firstresponeassignedresourceID", "objectPropertyPath": "name" }, - { "name": "LastActivityResourceID", "sourceType": "autotask-resource", "shape": "string", "visible": false }, + { "name": "lastActivityResourceID", "sourceType": "autotask-resource", "shape": "string", "visible": false }, { "name": "lastActivityResourceName", "displayName": "Last Activity Resource", "sourceId": "LastActivityResourceID", "objectPropertyPath": "name" }, { "name": "issueType", "displayName": "Issue Type" }, { "name": "resolution", "displayName": "Resolution" }, - { "name": "CreatorResourceID", "sourceType": "autotask-resource", "shape": "string", "visible": false }, + { "name": "creatorResourceID", "sourceType": "autotask-resource", "shape": "string", "visible": false }, { "name": "creatorResourceName", "displayName": "Creator", "sourceId": "CreatorResourceID", "objectPropertyPath": "name" }, { "name": "lastActivityDate", "displayName": "Last Activity Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, { "name": "dueDateTime", "displayName": "Due Date", "shape": ["date", { "timeZone": "Etc/UTC" }] }, diff --git a/plugins/AutoTask/v1/docs/README.md b/plugins/AutoTask/v1/docs/README.md index 0c2bb32..47c08b7 100644 --- a/plugins/AutoTask/v1/docs/README.md +++ b/plugins/AutoTask/v1/docs/README.md @@ -1,10 +1,10 @@ # Before you start -You will need an AutoTask account with admin access to create an API User. API Users are a dedicated account type in AutoTask used for system integrations — they are separate from regular user accounts and do not consume a standard user licence. +You will need an Autotask account with admin access to create an API User. API Users are a dedicated account type in Autotask used for system integrations — they are separate from regular user accounts and do not consume a standard user licence. ## Creating an API User -1. Log in to AutoTask and go to **Admin > Resources/Users (HR) > Resources** +1. Log in to Autotask and go to **Admin > Resources/Users (HR) > Resources** 2. Click **New** and select **API User** as the resource type 3. Complete the required fields, including an email address — this becomes the **API Username** 4. Under the **Credentials** section, click **Generate** next to **Integration Code** to create your **API Integration Code** @@ -15,9 +15,9 @@ The API User must have sufficient security permissions to read the entities you ## Finding your Zone URL -Your Zone URL is based on the AutoTask data centre your account is hosted on. To find it: +Your Zone URL is based on the Autotask data centre your account is hosted on. To find it: -1. Log in to AutoTask and look at the URL in your browser address bar — for example `https://ww14.autotask.net` +1. Log in to Autotask and look at the URL in your browser address bar — for example `https://ww14.autotask.net` 2. Replace `ww` with `webservices` to get the API base URL — for example `https://webservices14.autotask.net` Enter the full base including `https://`. @@ -26,7 +26,7 @@ Enter the full base including `https://`. | Field | Where to find it | |---|---| -| **Zone URL** | Derived from your AutoTask login URL — see above | +| **Zone URL** | Derived from your Autotask login URL — see above | | **API Integration Code** | Generated on the API User record under Credentials > Integration Code | | **API Username** | The email address entered when creating the API User | | **API Secret** | The password set on the API User record under Credentials | diff --git a/plugins/AutoTask/v1/metadata.json b/plugins/AutoTask/v1/metadata.json index 460b297..03e059c 100644 --- a/plugins/AutoTask/v1/metadata.json +++ b/plugins/AutoTask/v1/metadata.json @@ -1,13 +1,13 @@ { - "name": "auto-task", - "displayName": "AutoTask", - "version": "1.0.0", + "name": "autotask", + "displayName": "Autotask", + "version": "1.0.1", "author": { "name": "@TimWheeler-SQUP", "type": "community" }, - "description": "Monitor tickets, contracts, projects, resources, and company data from AutoTask PSA", - "category": "User Defined", + "description": "Monitor tickets, contracts, projects, resources, and company data from Datto Autotask PSA.", + "category": "Service Management", "type": "cloud", "schemaVersion": "2.0", "base": { @@ -36,8 +36,13 @@ "baseUrl": "{{zoneUrl}}" } }, - "keywords": ["autotask", "tickets", "psa", "itsm", "contracts", "projects"], + "keywords": ["autotask", "tickets", "psa", "itsm", "contracts", "projects","msp"], "links": [ + { + "category": "documentation", + "url": "https://github.com/squaredup/plugins/blob/main/plugins/AutoTask/v1/docs/README.md", + "label": "Help adding this plugin" + }, { "category": "source", "url": "https://github.com/squaredup/plugins/tree/main/plugins/AutoTask", diff --git a/plugins/AutoTask/v1/ui.json b/plugins/AutoTask/v1/ui.json index fbaac11..c9d8eb4 100644 --- a/plugins/AutoTask/v1/ui.json +++ b/plugins/AutoTask/v1/ui.json @@ -3,7 +3,7 @@ "type": "text", "name": "zoneUrl", "label": "Zone URL", - "help": "Your AutoTask zone URL — found in the address bar when logged into AutoTask, e.g. if your URL is `https://ww14.autotask.net` enter `https://webservices14.autotask.net`", + "help": "Found in the address bar when logged in to Autotask — e.g. if your login URL is `https://ww14.autotask.net` enter `https://webservices14.autotask.net`", "placeholder": "https://webservices16.autotask.net", "validation": { "required": true, @@ -15,7 +15,7 @@ "name": "userName", "label": "API Username", "placeholder": "apiuser@yourdomain.com", - "help": "The email address of your AutoTask API User", + "help": "Email address of the Autotask API User", "validation": { "required": true } @@ -24,7 +24,7 @@ "type": "password", "name": "integrationCode", "label": "API Integration Code", - "help": "Generated when creating an API User in AutoTask — Admin > Resources > API User", + "help": "Generated when creating an API User in Autotask — Admin > Resources > API User", "validation": { "required": true } @@ -33,7 +33,7 @@ "type": "password", "name": "secret", "label": "API Secret", - "help": "Generated when creating an API User in AutoTask", + "help": "Generated when creating an API User in Autotask", "validation": { "required": true } diff --git a/test-present.txt b/test-present.txt deleted file mode 100644 index 30d74d2..0000000 --- a/test-present.txt +++ /dev/null @@ -1 +0,0 @@ -test \ No newline at end of file