Files
api-collections/collections/IRIS GW (Gateway) REST API (v1)/Contracts/Get Contracts with two or more participants (scripted).yml
T
2026-02-18 15:04:39 +01:00

483 lines
12 KiB
YAML

info:
name: Get Contracts with two or more participants (scripted)
type: http
seq: 4
http:
method: GET
url: "{{IrisApiUrl}}/contracts;full?year=2025&wfState=signed&page=1"
params:
- name: pid
value: BELL_B_25_CCS_IST_RA_01
type: query
disabled: true
- name: pid
value: VIGN_G_25_CCS_COM_RA_FMT_01
type: query
disabled: true
- name: pid
value: BETT_S_25_CRCT_RA_CHIESI_01
type: query
disabled: true
- name: pid
value: BUSC_A_25_CRCT_RA_IRENAMBIENTE_01
type: query
disabled: true
- name: pid
value: RICC_A_25_CRCT_RA_KEMIN_01
type: query
disabled: true
- name: pid
value: GOBB_G_25_CONV_QUA_01
type: query
disabled: true
- name: pid
value: DELM_N_25_CRCT_RA_BAUMER_01
type: query
disabled: true
- name: pid
value: AMER_F_25_CCS_IST_RA_CODICI_01
type: query
disabled: true
- name: year
value: "2025"
type: query
- name: wfState
value: signed
type: query
- name: page
value: "1"
type: query
auth: inherit
runtime:
scripts:
- type: after-response
code: "// Aggregates contracts with 2+ contributors across all pages and reports summary.\r
// // Fixes TypeError: pm.request.url.query.toObject(...).find is not a function by avoiding Array.prototype.find on toObject() result.\r
// Adds robust helpers for working with query params across Postman SDK versions and edge cases.\r
//\r
// Requirements addressed:\r
// 1) Read Page-Count header to know total pages\r
// 2) Iteratively call same endpoint for pages from current+1 to Page-Count, preserving other query params\r
// 3) Aggregate items where contributorSet exists and has length >= 2\r
// 4) Build a map pid -> contributorCount across all pages (including initial)\r
// 5) Print summary in Test Results and set env var `multiContributorContracts` with [{ pid, contributorCount }]\r
// 6) Robust error handling, rate limiting, early stop on non-2xx; handle JSON array or paginated object response shapes\r
// 7) Augment aggregation with department (ownerSet[0].organizationUnit.idAb + \" - \" + description). Keep first non-empty.\r
\r
(function () {\r
\ const RATE_LIMIT_DELAY_MS = 200; // small delay between page fetches\r
\ const TARGET_ENV_VAR = 'multiContributorContracts';\r
\ const PAGE_PARAM = 'page';\r
\r
\ // ---------------- URL and Query helpers (SDK-safe) -----------------\r
\ // Returns a plain object of query params. Works with:\r
// // - pm.request.url.query (SDK v8+ as QueryList) using .toObject()\r
\ // - URL that has no query or is a raw string\r
\ function getQueryObject(url) {\r
\ try {\r
// // pm.request.url can be a Url object or a string. Normalize to raw string.\r
\ const raw = (typeof url === 'string') ? url : (url && (url.toString ? url.toString() : url.raw || ''));\r
\ // Try SDK path first if it's a Url object with .query\r
\ if (url && url.query && typeof url.query.toObject === 'function') {\r
\ const obj = url.query.toObject();\r
\ // toObject may return undefined/null on empty query\r
\ return obj && typeof obj === 'object' ? { ...obj } : {};\r
\ }\r
\ // Fallback: parse the raw string\r
\ if (typeof raw === 'string') {\r
\ const qIndex = raw.indexOf('?');\r
\ if (qIndex === -1) return {};\r
\ const queryStr = raw.substring(qIndex + 1);\r
\ if (!queryStr) return {};\r
\ return queryStr.split('&').reduce((acc, pair) => {\r
\ if (!pair) return acc;\r
\ const [k, v] = pair.split('=');\r
\ if (!k) return acc;\r
\ acc[decodeURIComponent(k)] = v !== undefined ? decodeURIComponent(v) : '';\r
\ return acc;\r
\ }, {});\r
\ }\r
\ } catch (e) {\r
\ // fallthrough\r
\ }\r
\ return {};\r
\ }\r
\r
\ function setQueryObject(url, updates) {\r
\ // Returns a new raw URL string with given query params merged\r
\ const raw = (typeof url === 'string') ? url : (url && (url.toString ? url.toString() : url.raw || ''));\r
\ const qIndex = raw.indexOf('?');\r
\ const base = qIndex === -1 ? raw : raw.substring(0, qIndex);\r
\ const current = getQueryObject(url);\r
\ const merged = { ...current, ...updates };\r
\ // Filter out empty/undefined to avoid adding stray keys\r
\ const parts = Object.keys(merged)\r
\ .filter(k => merged[k] !== undefined && merged[k] !== null && merged[k] !== '')\r
\ .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(String(merged[k])));\r
\ return parts.length ? base + '?' + parts.join('&') : base;\r
\ }\r
\r
\ // ---------------- Response shape helpers -----------------\r
\ function isObject(x) { return x && typeof x === 'object' && !Array.isArray(x); }\r
\r
\ function getItemsFromResponseBody(rb) {\r
\ // Supports either an array payload or an object with an array at known keys\r
\ if (Array.isArray(rb)) return rb;\r
\ if (isObject(rb)) {\r
\ // Try common keys: 'items', 'data', 'results'\r
\ if (Array.isArray(rb.items)) return rb.items;\r
\ if (Array.isArray(rb.data)) return rb.data;\r
\ if (Array.isArray(rb.results)) return rb.results;\r
\ }\r
\ return [];\r
\ }\r
\r
\ function safeJson(body) {\r
\ try { return JSON.parse(body); } catch (e) { return null; }\r
\ }\r
\r
\ // ---------------- Aggregation store -----------------\r
\ // Map: pid -> { contributorCount, department }\r
\ const aggregate = {};\r
\r
\ function extractDepartment(item) {\r
\ try {\r
\ const owner0 = Array.isArray(item.ownerSet) && item.ownerSet.length > 0 ? item.ownerSet[0] : null;\r
\ const ou = owner0 && owner0.organizationUnit ? owner0.organizationUnit : null;\r
\ const idAb = ou && typeof ou.idAb === 'string' ? ou.idAb : null;\r
\ const desc = ou && typeof ou.description === 'string' ? ou.description : null;\r
\ if (idAb && desc) return idAb + ' - ' + desc;\r
\ return '';\r
\ } catch (e) {\r
\ return '';\r
\ }\r
\ }\r
\r
\ function considerItems(items) {\r
\ items.forEach(it => {\r
\ const contributors = Array.isArray(it.contributorSet) ? it.contributorSet : [];\r
\ if (contributors.length >= 2) {\r
\ const pid = (it.pid != null) ? String(it.pid) : '';\r
\ if (!pid) return;\r
\ const contributorCount = contributors.length;\r
\ const dept = extractDepartment(it);\r
\ if (!aggregate[pid]) {\r
\ aggregate[pid] = { contributorCount, department: dept || '' };\r
\ } else {\r
\ // keep max contributor count seen (in case of variations) and first non-empty department\r
\ aggregate[pid].contributorCount = Math.max(aggregate[pid].contributorCount, contributorCount);\r
\ if (!aggregate[pid].department && dept) {\r
\ aggregate[pid].department = dept;\r
\ }\r
\ }\r
\ }\r
\ });\r
\ }\r
\r
\ // ---------------- Paging orchestration -----------------\r
\ const initialStatus = res.getStatus();\r
\ const is2xx = initialStatus >= 200 && initialStatus < 300;\r
\ if (!is2xx) {\r
\ test('Request failed - not aggregating on non-2xx', function () {\r
\ expect(is2xx).to.eql(true);\r
\ });\r
\ return;\r
\ }\r
\r
\ const rb = safeJson(JSON.stringify(res.getBody()));\r
\ const initialItems = rb ? getItemsFromResponseBody(rb) : [];\r
\ considerItems(initialItems);\r
\r
\ // Derive total pages from header 'Page-Count' or 'page-count'\r
\ const pageCountHeader = res.getHeader('Page-Count') || res.getHeader('page-count') || res.getHeader('X-Total-Pages');\r
\ const totalPages = pageCountHeader ? parseInt(pageCountHeader, 10) : 1;\r
\r
\ // Figure out current page from request URL (query param 'page')\r
\ const currentQuery = getQueryObject(req.getUrl());\r
\ const currentPage = parseInt(currentQuery[PAGE_PARAM] || '1', 10) || 1;\r
\r
\ // Build a function that fetches page N and aggregates\r
\ function fetchPage(n) {\r
\ return new Promise((resolve) => {\r
\ const nextUrl = setQueryObject(req.getUrl(), { [PAGE_PARAM]: String(n) });\r
\ setTimeout(function () {\r
\ await bru.sendRequest({ url: nextUrl, method: 'GET' }, async function(err, res) {\r
\ if (err || !res) {\r
\ test('Error fetching page ' + n, function () {\r
\ expect(err).to.eql(null);\r
\ });\r
\ return resolve(false);\r
\ }\r
\ const ok = res.status >= 200 && res.status < 300;\r
\ if (!ok) {\r
\ test('Non-2xx on page ' + n + ' - stop further paging', function () {\r
\ expect(ok).to.eql(true);\r
\ });\r
\ return resolve(false);\r
\ }\r
\ const body = res.data;\r
\ const json = safeJson(body);\r
\ const items = json ? getItemsFromResponseBody(json) : [];\r
\ considerItems(items);\r
\ resolve(true);\r
\ });\r
\ }, RATE_LIMIT_DELAY_MS);\r
\ });\r
\ }\r
\r
\ async function run() {\r
\ // If there are more pages, iterate\r
\ for (let p = currentPage + 1; p <= totalPages; p++) {\r
\ const cont = await fetchPage(p);\r
\ if (!cont) break;\r
\ }\r
\r
\ // Prepare output array\r
\ const output = Object.keys(aggregate).map(pid => ({\r
\ pid,\r
\ contributorCount: aggregate[pid].contributorCount,\r
\ department: aggregate[pid].department || ''\r
\ }));\r
\r
\ // Save to environment\r
\ bru.setEnvVar(TARGET_ENV_VAR, JSON.stringify(output));\r
\r
\ // Basic summary tests\r
\ test('Aggregated items have required properties', function () {\r
\ output.forEach(item => {\r
\ expect(item).to.have.property('pid');\r
\ expect(item.pid).to.be.a('string');\r
\ expect(item).to.have.property('contributorCount');\r
\ expect(item.contributorCount).to.be.a('number');\r
\ expect(item).to.have.property('department');\r
\ expect(item.department).to.be.a('string');\r
\ });\r
\ });\r
\r
\ // Optional: Log summary count\r
\ test('Total multi-contributor contracts aggregated', function () {\r
\ expect(output.length).to.be.at.least(0);\r
\ });\r
\ }\r
\r
\ run();\r
})();"
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5