From 21341ec52d79980eace1cccaa0488d4cbc1645dc Mon Sep 17 00:00:00 2001 From: Pier-Paolo Mammi Date: Fri, 10 Jul 2026 15:55:46 +0200 Subject: [PATCH] cleanup contracts requests --- .../Contracts/Get Contracts FULL.yml | 96 ++---- ...th two or more participants (scripted).yml | 283 ------------------ .../Contracts/Get Contracts.yml | 13 +- .../Contracts/folder.yml | 11 + 4 files changed, 47 insertions(+), 356 deletions(-) delete mode 100644 collections/IRIS GW (Gateway) REST API (v1)/Contracts/Get Contracts with two or more participants (scripted).yml diff --git a/collections/IRIS GW (Gateway) REST API (v1)/Contracts/Get Contracts FULL.yml b/collections/IRIS GW (Gateway) REST API (v1)/Contracts/Get Contracts FULL.yml index 629fab8..b1e816c 100644 --- a/collections/IRIS GW (Gateway) REST API (v1)/Contracts/Get Contracts FULL.yml +++ b/collections/IRIS GW (Gateway) REST API (v1)/Contracts/Get Contracts FULL.yml @@ -5,78 +5,10 @@ info: http: method: GET - url: "{{IrisApiUrl}}/contracts;full?pid=VERO_L_26_CRCT_RA_AOUPR_01" + url: "{{IrisApiUrl}}/contracts;full?pid=SEGA_A_25_FORM_COM_01" 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: wfState - value: validated - type: query - disabled: true - - name: page - value: "1" - type: query - disabled: true - - name: year - value: "2025" - type: query - disabled: true - - name: pid - value: MAMB_C_25_ACC_ACC_PROROGACONTRATTOCATAGLOGAZIO_01 - type: query - disabled: true - - name: pid - value: ZERB_A_25_SERV_CON_ASSUNTA_01 - type: query - disabled: true - - name: pid - value: COVA_P_24_SERV_CON_01 - type: query - disabled: true - - name: pid - value: GIUL_F_25_CRCT_RA_MOVYON_01 - type: query - disabled: true - - name: pid - value: LONG_S_25_SERV_CON_POLIMI_01 - type: query - disabled: true - - name: pid - value: DONO_G_25_ACC_ACC_ZOETIS_01 - type: query - disabled: true - - name: pid - value: VERO_L_26_CRCT_RA_AOUPR_01 + value: SEGA_A_25_FORM_COM_01 type: query auth: inherit @@ -2192,3 +2124,27 @@ examples: body: type: json data: "[]" + +docs: | + ## Examples + + | name | value | note | + | ---- | ------------------------------------------------- | ----------------------- | + | pid | BELL_B_25_CCS_IST_RA_01 | | + | pid | VIGN_G_25_CCS_COM_RA_FMT_01 | | + | pid | BETT_S_25_CRCT_RA_CHIESI_01 | | + | pid | BUSC_A_25_CRCT_RA_IRENAMBIENTE_01 | | + | pid | RICC_A_25_CRCT_RA_KEMIN_01 | | + | pid | GOBB_G_25_CONV_QUA_01 | | + | pid | DELM_N_25_CRCT_RA_BAUMER_01 | | + | pid | AMER_F_25_CCS_IST_RA_CODICI_01 | | + | pid | MAMB_C_25_ACC_ACC_PROROGACONTRATTOCATAGLOGAZIO_01 | | + | pid | ZERB_A_25_SERV_CON_ASSUNTA_01 | | + | pid | COVA_P_24_SERV_CON_01 | | + | pid | GIUL_F_25_CRCT_RA_MOVYON_01 | | + | pid | LONG_S_25_SERV_CON_POLIMI_01 | | + | pid | DONO_G_25_ACC_ACC_ZOETIS_01 | | + | pid | VERO_L_26_CRCT_RA_AOUPR_01 | | + | pid | SEGA_A_25_FORM_COM_01 | | + | pid | BELL_B_26_CCS_IST_RA_AGENZIAREGIONALEPERLASICU_01 | 4 owner + 3 contributor | + | pid | GIUL_F_25_CRCT_RA_MOVYON_01 | 2 owner + 7 contributor | diff --git a/collections/IRIS GW (Gateway) REST API (v1)/Contracts/Get Contracts with two or more participants (scripted).yml b/collections/IRIS GW (Gateway) REST API (v1)/Contracts/Get Contracts with two or more participants (scripted).yml deleted file mode 100644 index c0a4432..0000000 --- a/collections/IRIS GW (Gateway) REST API (v1)/Contracts/Get Contracts with two or more participants (scripted).yml +++ /dev/null @@ -1,283 +0,0 @@ -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: year - value: "2025" - type: query - - name: wfState - value: signed - type: query - - name: page - value: "1" - type: query - - 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 - auth: inherit - -runtime: - scripts: - - type: after-response - code: |- - // Aggregates contracts with 2+ contributors across all pages and reports summary. - // // Fixes TypeError: pm.request.url.query.toObject(...).find is not a function by avoiding Array.prototype.find on toObject() result. - // Adds robust helpers for working with query params across Postman SDK versions and edge cases. - // - // Requirements addressed: - // 1) Read Page-Count header to know total pages - // 2) Iteratively call same endpoint for pages from current+1 to Page-Count, preserving other query params - // 3) Aggregate items where contributorSet exists and has length >= 2 - // 4) Build a map pid -> contributorCount across all pages (including initial) - // 5) Print summary in Test Results and set env var `multiContributorContracts` with [{ pid, contributorCount }] - // 6) Robust error handling, rate limiting, early stop on non-2xx; handle JSON array or paginated object response shapes - // 7) Augment aggregation with department (ownerSet[0].organizationUnit.idAb + " - " + description). Keep first non-empty. - - (function () { - const RATE_LIMIT_DELAY_MS = 200; // small delay between page fetches - const TARGET_ENV_VAR = 'multiContributorContracts'; - const PAGE_PARAM = 'page'; - - // ---------------- URL and Query helpers (SDK-safe) ----------------- - // Returns a plain object of query params. Works with: - // - pm.request.url.query (SDK v8+ as QueryList) using .toObject() - // - URL that has no query or is a raw string - function getQueryObject(url) { - try { - // // pm.request.url can be a Url object or a string. Normalize to raw string. - const raw = (typeof url === 'string') ? url : (url && (url.toString ? url.toString() : url.raw || '')); - // Try SDK path first if it's a Url object with .query - if (url && url.query && typeof url.query.toObject === 'function') { - const obj = url.query.toObject(); - // toObject may return undefined/null on empty query - return obj && typeof obj === 'object' ? { ...obj } : {}; - } - // Fallback: parse the raw string - if (typeof raw === 'string') { - const qIndex = raw.indexOf('?'); - if (qIndex === -1) return {}; - const queryStr = raw.substring(qIndex + 1); - if (!queryStr) return {}; - return queryStr.split('&').reduce((acc, pair) => { - if (!pair) return acc; - const [k, v] = pair.split('='); - if (!k) return acc; - acc[decodeURIComponent(k)] = v !== undefined ? decodeURIComponent(v) : ''; - return acc; - }, {}); - } - } catch (e) { - // fallthrough - } - return {}; - } - - function setQueryObject(url, updates) { - // Returns a new raw URL string with given query params merged - const raw = (typeof url === 'string') ? url : (url && (url.toString ? url.toString() : url.raw || '')); - const qIndex = raw.indexOf('?'); - const base = qIndex === -1 ? raw : raw.substring(0, qIndex); - const current = getQueryObject(url); - const merged = { ...current, ...updates }; - // Filter out empty/undefined to avoid adding stray keys - const parts = Object.keys(merged) - .filter(k => merged[k] !== undefined && merged[k] !== null && merged[k] !== '') - .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(String(merged[k]))); - return parts.length ? base + '?' + parts.join('&') : base; - } - - // ---------------- Response shape helpers ----------------- - function isObject(x) { return x && typeof x === 'object' && !Array.isArray(x); } - - function getItemsFromResponseBody(rb) { - // Supports either an array payload or an object with an array at known keys - if (Array.isArray(rb)) return rb; - if (isObject(rb)) { - // Try common keys: 'items', 'data', 'results' - if (Array.isArray(rb.items)) return rb.items; - if (Array.isArray(rb.data)) return rb.data; - if (Array.isArray(rb.results)) return rb.results; - } - return []; - } - - function safeJson(body) { - try { return JSON.parse(body); } catch (e) { return null; } - } - - // ---------------- Aggregation store ----------------- - // Map: pid -> { contributorCount, department } - const aggregate = {}; - - function extractDepartment(item) { - try { - const owner0 = Array.isArray(item.ownerSet) && item.ownerSet.length > 0 ? item.ownerSet[0] : null; - const ou = owner0 && owner0.organizationUnit ? owner0.organizationUnit : null; - const idAb = ou && typeof ou.idAb === 'string' ? ou.idAb : null; - const desc = ou && typeof ou.description === 'string' ? ou.description : null; - if (idAb && desc) return idAb + ' - ' + desc; - return ''; - } catch (e) { - return ''; - } - } - - function considerItems(items) { - items.forEach(it => { - const contributors = Array.isArray(it.contributorSet) ? it.contributorSet : []; - if (contributors.length >= 2) { - const pid = (it.pid != null) ? String(it.pid) : ''; - if (!pid) return; - const contributorCount = contributors.length; - const dept = extractDepartment(it); - if (!aggregate[pid]) { - aggregate[pid] = { contributorCount, department: dept || '' }; - } else { - // keep max contributor count seen (in case of variations) and first non-empty department - aggregate[pid].contributorCount = Math.max(aggregate[pid].contributorCount, contributorCount); - if (!aggregate[pid].department && dept) { - aggregate[pid].department = dept; - } - } - } - }); - } - - // ---------------- Paging orchestration ----------------- - const initialStatus = res.getStatus(); - const is2xx = initialStatus >= 200 && initialStatus < 300; - if (!is2xx) { - test('Request failed - not aggregating on non-2xx', function () { - expect(is2xx).to.eql(true); - }); - return; - } - - const rb = safeJson(JSON.stringify(res.getBody())); - const initialItems = rb ? getItemsFromResponseBody(rb) : []; - considerItems(initialItems); - - // Derive total pages from header 'Page-Count' or 'page-count' - const pageCountHeader = res.getHeader('Page-Count') || res.getHeader('page-count') || res.getHeader('X-Total-Pages'); - const totalPages = pageCountHeader ? parseInt(pageCountHeader, 10) : 1; - - // Figure out current page from request URL (query param 'page') - const currentQuery = getQueryObject(req.getUrl()); - const currentPage = parseInt(currentQuery[PAGE_PARAM] || '1', 10) || 1; - - // Build a function that fetches page N and aggregates - function fetchPage(n) { - return new Promise((resolve) => { - //console.log("Setting current page to: " + n); - const nextUrl = setQueryObject(req.getUrl(), { [PAGE_PARAM]: String(n) }); - const reqHeaders = req.getHeaders - - console.log("Setting headers: " + JSON.stringify(reqHeaders)); - setTimeout(async function () { - console.log("Calling next URL... (" + nextUrl + ")"); - await bru.sendRequest({ url: nextUrl, method: 'GET', headers: reqHeaders }, async function(err, res) { - if (err || !res) { - console.log("Error fetching page " + n + " (err: " + JSON.stringify(err) + ")"); - test('Error fetching page ' + n, function () { - expect(err).to.eql(null); - }); - return resolve(false); - } - const ok = res.status >= 200 && res.status < 300; - console.log("Next URL response: " + ok); - if (!ok) { - test('Non-2xx on page ' + n + ' - stop further paging', function () { - expect(ok).to.eql(true); - }); - return resolve(false); - } - const body = res.data; - const json = safeJson(body); - const items = json ? getItemsFromResponseBody(json) : []; - considerItems(items); - return resolve(true); - }); - - }, RATE_LIMIT_DELAY_MS); - }); - } - - async function run() { - console.log("Running..."); - console.log("Total pages found: " + totalPages); - // If there are more pages, iterate - for (let p = currentPage + 1; p <= totalPages; p++) { - const cont = await fetchPage(p); - if (!cont) break; - } - - // Prepare output array - const output = Object.keys(aggregate).map(pid => ({ - pid, - contributorCount: aggregate[pid].contributorCount, - department: aggregate[pid].department || '' - })); - - // Save to environment - bru.setVar(TARGET_ENV_VAR, JSON.stringify(output)); - - // Basic summary tests - test('Aggregated items have required properties', function () { - output.forEach(item => { - expect(item).to.have.property('pid'); - expect(item.pid).to.be.a('string'); - expect(item).to.have.property('contributorCount'); - expect(item.contributorCount).to.be.a('number'); - expect(item).to.have.property('department'); - expect(item.department).to.be.a('string'); - }); - }); - - // Optional: Log summary count - test('Total multi-contributor contracts aggregated', function () { - expect(output.length).to.be.at.least(0); - }); - } - - run(); - })(); - -settings: - encodeUrl: true - timeout: 0 - followRedirects: true - maxRedirects: 5 diff --git a/collections/IRIS GW (Gateway) REST API (v1)/Contracts/Get Contracts.yml b/collections/IRIS GW (Gateway) REST API (v1)/Contracts/Get Contracts.yml index 2674be5..e0c5c00 100644 --- a/collections/IRIS GW (Gateway) REST API (v1)/Contracts/Get Contracts.yml +++ b/collections/IRIS GW (Gateway) REST API (v1)/Contracts/Get Contracts.yml @@ -5,7 +5,7 @@ info: http: method: GET - url: "{{IrisApiUrl}}/contracts?page={{currentPage}}&sort=pid&dir=asc&wfState=closed&wfState=signed" + url: "{{IrisApiUrl}}/contracts?person.cf=GLNFLC70S16I158E&person.relation=contributor&page={{currentPage}}&pageSize=20&sort=pid&dir=asc" params: - name: id value: "123" @@ -48,6 +48,9 @@ http: value: "" type: query disabled: true + - name: person.cf + value: GLNFLC70S16I158E + type: query - name: person.id value: "" type: query @@ -69,10 +72,9 @@ http: type: query disabled: true - name: person.relation - value: test + value: contributor type: query description: owner|contributor - disabled: true - name: department.id value: "" type: query @@ -108,6 +110,9 @@ http: - name: page value: "{{currentPage}}" type: query + - name: pageSize + value: "20" + type: query - name: sort value: pid type: query @@ -117,9 +122,11 @@ http: - name: wfState value: closed type: query + disabled: true - name: wfState value: signed type: query + disabled: true auth: inherit settings: diff --git a/collections/IRIS GW (Gateway) REST API (v1)/Contracts/folder.yml b/collections/IRIS GW (Gateway) REST API (v1)/Contracts/folder.yml index d6c21a7..7af8a06 100644 --- a/collections/IRIS GW (Gateway) REST API (v1)/Contracts/folder.yml +++ b/collections/IRIS GW (Gateway) REST API (v1)/Contracts/folder.yml @@ -5,3 +5,14 @@ info: request: auth: inherit + +docs: + content: |- + # IRIS GW (Gateway) REST API documentation (v1) + + Link: + + ## Contratti + + Link: + type: text/markdown