From 1946f79eef678174ac7a01d8d2613e702279f230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Hofman?= Date: Tue, 27 Feb 2024 16:42:27 +0100 Subject: [PATCH] Add form-data and qs dependencies, update getAccessTokenFromLoginEndpoint function, and modify openapi.yml and component.json files --- src/appmixer/daktela/auth.js | 7 ++++- src/appmixer/daktela/openapi.yml | 24 ++++++++++++-- src/appmixer/daktela/package.json | 3 +- .../daktela/tickets/getTickets/component.json | 22 +++++++++++-- .../daktela/tickets/getTickets/getTickets.js | 31 +++++++++++++++---- 5 files changed, 75 insertions(+), 12 deletions(-) diff --git a/src/appmixer/daktela/auth.js b/src/appmixer/daktela/auth.js index c0de40a5b..a71b2fafb 100644 --- a/src/appmixer/daktela/auth.js +++ b/src/appmixer/daktela/auth.js @@ -41,7 +41,12 @@ module.exports = { } }, - // This function is called in every component to get the access token from the login endpoint. + /** + * Function called in every component to get the access token from the login endpoint. + * It is cached so that we don't have to call the login endpoint in every component. + * Access token changes only when the user changes the password. + * @returns Access token + */ getAccessTokenFromLoginEndpoint: async function(context) { const cacheKey = `daktela-access-token-${context.username || context.auth.username}`; diff --git a/src/appmixer/daktela/openapi.yml b/src/appmixer/daktela/openapi.yml index 45bb5ec1c..4ebdc4c11 100644 --- a/src/appmixer/daktela/openapi.yml +++ b/src/appmixer/daktela/openapi.yml @@ -27,16 +27,36 @@ paths: parameters: offset: skip limit: take - page: 100 + # Ticket objects are quite large, so we limit the number of items per page + page: 10 results: result.data - count: result.count + count: total parameters: + - name: q + in: query + required: false + description: Search query. + schema: + type: string - name: filter in: query required: false description: Filter tickets. See [Daktela API documentation](https://democz.daktela.com/external/apihelp/v6/working-with/tickets#example-1-url) for more information. + schema: + type: array + example: [{"field":"status","operator":"eq","value":"OPEN"}] + items: + type: object + - name: sort + in: query + required: false + description: | + Sorting is defined as an array of columns by which the data can be sorted. Each array has its own sorting name and each item within its range has two properties: + - field - string - Name of the column by which you want to sort + - dir - string - Direction of sorting 'asc' (sort in a ascending order) or 'desc' (sort in a descending order) schema: type: string + example: {"sort": [{"field":"firstname", "dir":"desc"},{"field":"lastname", "dir":"asc"}]} responses: '200': description: A list of tickets diff --git a/src/appmixer/daktela/package.json b/src/appmixer/daktela/package.json index b5d02d195..3c352aaf9 100644 --- a/src/appmixer/daktela/package.json +++ b/src/appmixer/daktela/package.json @@ -6,6 +6,7 @@ "jsonata": "2.0.3", "json-pointer": "0.6.2", "jmespath": "0.16.0", - "form-data": "4.0.0" + "form-data": "4.0.0", + "qs": "6.11.2" } } \ No newline at end of file diff --git a/src/appmixer/daktela/tickets/getTickets/component.json b/src/appmixer/daktela/tickets/getTickets/component.json index 013700658..04eb4c204 100644 --- a/src/appmixer/daktela/tickets/getTickets/component.json +++ b/src/appmixer/daktela/tickets/getTickets/component.json @@ -11,9 +11,15 @@ "schema": { "type": "object", "properties": { + "q": { + "type": "string" + }, "filter": { "type": "string" }, + "sort": { + "type": "string" + }, "xConnectorOutputType": { "type": "string" }, @@ -24,11 +30,23 @@ }, "inspector": { "inputs": { + "q": { + "type": "text", + "index": 0, + "label": "Search", + "tooltip": "

Search query.

" + }, "filter": { "type": "textarea", - "index": 0, + "index": 1, "label": "Filter", - "tooltip": "

Filter tickets. See Daktela API documentation for more information.

" + "tooltip": "

Filter tickets. See Daktela API documentation for more information.

Example: {\"filter\":[{\"field\":\"stage\",\"operator\":\"eq\",\"value\":\"OPEN\"}]}

" + }, + "sort": { + "type": "textarea", + "index": 2, + "label": "Sort", + "tooltip": "

Sorting is defined as an array of columns by which the data can be sorted. Each array has its own sorting name and each item within its range has two properties:

\n" }, "xConnectorOutputType": { "label": "Output Options", diff --git a/src/appmixer/daktela/tickets/getTickets/getTickets.js b/src/appmixer/daktela/tickets/getTickets/getTickets.js index 824d29572..a65b3d2bf 100644 --- a/src/appmixer/daktela/tickets/getTickets/getTickets.js +++ b/src/appmixer/daktela/tickets/getTickets/getTickets.js @@ -2,6 +2,7 @@ const lib = require('../../lib'); const { getAccessTokenFromLoginEndpoint } = require('../../auth'); +const qs = require('qs'); module.exports = { @@ -13,7 +14,7 @@ module.exports = { const limit = context.messages.in.content.xConnectorPaginationLimit; const query = { - 'take': 100 , + 'take': 10 , 'skip': 0 }; let data; @@ -29,7 +30,7 @@ module.exports = { result = page.slice(0, limit); hasMore = result.length > 0; - const countExpression = lib.jsonata('result.count'); + const countExpression = lib.jsonata('total'); let count = await countExpression.evaluate(data); hasMore = hasMore && result.length < count; needMore = result.length < limit; @@ -37,7 +38,7 @@ module.exports = { let failsafe = 0; // Repeat for other pages. while (hasMore && needMore && failsafe < limit) { - query['skip'] += 100; + query['skip'] += 10; ({ data } = await this.httpRequest(context, { query })); page = await pageExpression.evaluate(data); result = result.concat(page); @@ -66,7 +67,11 @@ module.exports = { const headers = {}; const query = new URLSearchParams; - const queryParameters = { 'filter': input['filter'] }; + const queryParameters = { + q: input['q'], + // 'filter': input['filter'], Done separately + 'sort': input['sort'] + }; if (override?.query) { Object.keys(override.query).forEach(parameter => { @@ -94,7 +99,21 @@ module.exports = { if (override.headers) req.headers = override.headers; if (override.method) req.method = override.method; - const queryString = query.toString(); + let queryString = query.toString(); + const inputFilter = input['filter']?.trim(); + if (inputFilter) { + try { + const filterObject = JSON.parse(input['filter']); + const qsFilter = qs.stringify(filterObject, { encode: true }); + if (qsFilter) { + queryString += '&' + qsFilter; + } + } catch (e) { + // context.log({ step: 'Error parsing filter object', error: e }); + throw new context.CancelError('Error parsing filter object', e); + } + } + if (queryString) { req.url += '?' + queryString; } @@ -123,7 +142,7 @@ module.exports = { step: 'http-request-error', request: { url: req.url, - method: req.method, + headers: req.headers, data: req.data },