add other collections

This commit is contained in:
2026-02-18 15:04:39 +01:00
parent 2980a31d86
commit 136dc82ab2
136 changed files with 173569 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,482 @@
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
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,7 @@
info:
name: Contracts
type: folder
seq: 2
request:
auth: inherit
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,7 @@
info:
name: WfItems
type: folder
seq: 3
request:
auth: inherit
@@ -0,0 +1,116 @@
info:
name: "[Runner] Get All Contracts"
type: http
seq: 1
http:
method: GET
url: "{{IrisApiUrl}}/contracts;full?page={{runnerCurrentPage}}&sort=pid&dir=asc"
params:
- name: page
value: "{{runnerCurrentPage}}"
type: query
- name: sort
value: pid
type: query
description: |-
The usable fields for sorting are:
- id
- pid
- year
- name
- startDate
- lastModified
- wfItemType.identifier (only for item with validation flow)
- wfItemType.description (only for item with validation flow)
The actual usable sorting filters for a specific resource type are specified in the REST Service Details
- name: dir
value: asc
type: query
auth: inherit
runtime:
scripts:
- type: after-response
code: "test(\"Status code is 200\", function () {\r
\ expect(res.getStatus()).to.equal(200);\r
\r
\ // Get pagination headers\r
\ var currentPage = parseInt(res.getHeader(\"Page\"));\r
\ var totalPages = parseInt(res.getHeader(\"Page-Count\"));\r
\r
\ // If first loop, reset results array\r
\ if (currentPage == 1)\r
\ {\r
\ bru.setVar(\"runnerResultsArray\", JSON.stringify([]));\r
\ }\r
\r
\ // Update result array\r
\ let resultsArray = JSON.parse(bru.getVar(\"runnerResultsArray\"));\r
\ var results = res.getBody();\r
\ resultsArray = resultsArray.concat(results);\r
\ bru.setVar(\"runnerResultsArray\", JSON.stringify(resultsArray));\r
\ \r
\ // Loop decision\r
\ if (currentPage == totalPages)\r
\ {\r
\ // Loop has finished, reset variables and stop\r
\ bru.setVar(\"runnerCurrentPage\", 1);\r
\ bru.deleteVar(\"runnerTotalPages\");\r
// //pm.collectionVariables.unset(\"runnerResultsArray\"); // Do not unset, so you can find results in the env var!\r
\ }\r
\ else\r
\ {\r
\ // Still having pages, update pagination values\r
\ bru.setVar(\"runnerCurrentPage\", ++currentPage);\r
\ bru.setVar(\"runnerTotalPages\", totalPages);\r
\r
\ // Set next request and wait for 1 second before sending the next request. This is to avoid hitting the rate limit.\r
\ bru.runner.setNextRequest(req.getName());\r
\ setTimeout(function(){}, [1000]);\r
\ }\r
});"
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5
@@ -0,0 +1,7 @@
info:
name: "[Runner] Get All Contracts"
type: folder
seq: 1
request:
auth: inherit
@@ -0,0 +1,400 @@
opencollection: 1.0.0
info:
name: IRIS GW (Gateway) REST API (v1)
config:
proxy:
inherit: true
config:
protocol: http
hostname: ""
port: ""
auth:
username: ""
password: ""
bypassProxy: ""
request:
auth:
type: basic
username: "{{IrisApiUsername}}"
password: "{{IrisApiPassword}}"
variables:
- name: runnerCurrentPage
value: "1"
- name: runnerResultsArray
value: ""
scripts:
- type: after-response
code: |-
// GO TO THE END OF THE TEST SCRIPT.
var template = String.raw`
<style>
.fill,
body,
html {
height: 100%
}
#json_vl,
.td_head,
.td_row_even,
.td_row_odd {
font-size: small
}
#json_pnl,
#xxa,
.navbar-header,
.navbar-nav,
.navbar-nav>li,
.td_head {
float: left
}
.fill {
min-height: 100%
}
#json_vl {
font-family: Consolas, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace
}
#widget {
width: 100%
}
.top_size {
height: 51px
}
#all_panels {
height: 100%;
min-height: 100%
}
#aboutLnk {
position: fixed;
right: 10px;
top: 15px
}
#inner_text {
display: block;
position: absolute;
height: auto;
bottom: 0;
top: 0;
left: 0;
right: 0;
margin-top: 51px;
margin-bottom: 0
}
#json_pnl {
background-color: #ccc;
width: 33.3%
}
#xxa {
background-color: #E8E8E8;
width: 66.7%
}
#table_pnl,
#tree_pnl {
background-color: #E8E8E8;
float: left;
width: 50%
}
#sharethis {
position: fixed;
right: 80px;
top: 10px
}
#inner_tbl {
padding-left: 2px
}
.td_row_even {
padding: 2px;
background-color: #F6F4F0
}
.td_row_odd {
padding: 2px;
background-color: #FFF
}
.td_head {
padding: 2px;
font-weight: 700
}
input,
p,
select,
td,
textarea,
th {
font-size: 1em
}
table,
td,
th {
border: 1px solid gray
}
textarea {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
display: block;
width: 100%;
height: 100%;
margin: 0;
padding: 4px;
border: 1px solid #333;
overflow-y: auto;
overflow-x: hidden
}
*,
html {
font-family: Verdana, Arial, Helvetica, sans-serif
}
form,
h1,
h2,
h3,
h4,
h5,
li,
p,
ul {
margin: 0;
padding: 0
}
img {
border: none
}
p {
margin: 0 0 1em
}
table {
font-size: 100%;
background-color: white;
}
ol.tree {
padding: 0 0 0 30px;
width: 300px
}
li {
position: relative;
margin-left: -15px;
list-style: none
}
li.file {
margin-left: -1px !important
}
li.file a {
background: url(leaf.png)0 5px no-repeat;
color: #000;
padding-left: 12px;
text-decoration: none;
display: block;
font-size: small
}
li.file a[href$='.css'],
li.file a[href$='.js'],
li.file a[href*='.pdf'],
li.file a[href*='.html'] {
background: url(http://www.thecssninja.com/demo/css_tree/document.png)no-repeat
}
li input {
position: absolute;
left: 0;
margin-left: 0;
opacity: 0;
z-index: 2;
cursor: pointer;
height: 1em;
width: 1em;
top: 0
}
li input+ol {
background: url(http://www.thecssninja.com/demo/css_tree/toggle-small-expand.png)40px -3px no-repeat;
margin: -20px 0 0 -44px;
height: 1em;
padding: 1.563em 0 0 80px
}
li label.lbl_array,
li label.lbl_obj {
display: block;
padding-left: 33px;
margin-bottom: 2px
}
li input+ol>li {
display: none;
margin-left: -14px !important;
padding-left: 1px
}
li label.lbl_obj {
background: url(folder.png)15px 1px no-repeat;
cursor: pointer
}
li label.lbl_array {
background: url(array.png)15px 1px no-repeat;
cursor: pointer
}
li input:checked+ol {
background: url(http://www.thecssninja.com/demo/css_tree/toggle-small.png)40px -3px no-repeat;
margin: -20px 0 0 -44px;
padding: 1.563em 0 0 80px;
height: auto
}
li input:checked+ol>li {
display: block;
margin: 0 0 .125em
}
li input:checked+ol>li:last-child {
margin: 0 0 .063em
}
.container {
width: 970px;
max-width: none !important
}
.col-xs-4 {
padding-top: 15px;
padding-bottom: 15px;
background-color: #eee;
background-color: rgba(86, 61, 124, .15);
border: 1px solid #ddd;
border: 1px solid rgba(86, 61, 124, .2)
}
</style>
<div id="html">
<input type="text" id="json">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!--<script src="json2table.js"></script>-->
<script>
$(function() {
// pm.getData((err, data) => {
$("#json").val(JSON.stringify(data.json));
json2table("#html");
});
});
function call(a) {
$("#json").val(JSON.stringify(a, void 0, 2));
json2table()
}
function json2table(selector) {
$(selector).html(buildTable(getJsonVar()));
}
function getJsonVar() {
try {
var a = $.parseJSON($("#json").val());
$("#json").val(JSON.stringify(a, void 0, 2));
return a
} catch (e) {
//return $("#error_msg").text(e.message), $("#errorModal").modal("show"), {}
alert(e);
}
}
function buildTable(a) {
var e = document.createElement("table"),
d, b;
if (isArray(a)) return buildArray(a);
for (var c in a) "object" != typeof a[c] || isArray(a[c]) ? "object" == typeof a[c] && isArray(a[c]) ? (d = e.insertRow(-1), b = d.insertCell(-1), b.colSpan = 2, b.innerHTML = '<div class="td_head">' + encodeText(c) + '</div><table style="width:100%">' + $(buildArray(a[c]), !1).html() + "</table>") : (d = e.insertRow(-1), b = d.insertCell(-1), b.innerHTML = "<div class='td_head'>" + encodeText(c) + "</div>", d = d.insertCell(-1), d.innerHTML = "<div class='td_row_even'>" +
encodeText(a[c]) + "</div>") : (d = e.insertRow(-1), b = d.insertCell(-1), b.colSpan = 2, b.innerHTML = '<div class="td_head">' + encodeText(c) + '</div><table style="width:100%">' + $(buildTable(a[c]), !1).html() + "</table>");
return e
}
function buildArray(a) {
var e = document.createElement("table"),
d, b, c = !1,
p = !1,
m = {},
h = -1,
n = 0,
l;
l = "";
if (0 == a.length) return "<div></div>";
d = e.insertRow(-1);
for (var f = 0; f < a.length; f++)
if ("object" != typeof a[f] || isArray(a[f])) "object" == typeof a[f] && isArray(a[f]) ? (b = d.insertCell(h), b.colSpan = 2, b.innerHTML = '<div class="td_head"></div><table style="width:100%">' + $(buildArray(a[f]), !1).html() + "</table>", c = !0) : p || (h += 1, p = !0, b = d.insertCell(h), m.empty = h, b.innerHTML = "<div class='td_head'>&nbsp;</div>");
else
for (var k in a[f]) l =
"-" + k, l in m || (c = !0, h += 1, b = d.insertCell(h), m[l] = h, b.innerHTML = "<div class='td_head'>" + encodeText(k) + "</div>");
c || e.deleteRow(0);
n = h + 1;
for (f = 0; f < a.length; f++)
if (d = e.insertRow(-1), td_class = isEven(f) ? "td_row_even" : "td_row_odd", "object" != typeof a[f] || isArray(a[f]))
if ("object" == typeof a[f] && isArray(a[f]))
for (h = m.empty, c = 0; c < n; c++) b = d.insertCell(c), b.className = td_class, l = c == h ? '<table style="width:100%">' + $(buildArray(a[f]), !1).html() + "</table>" : " ", b.innerHTML = "<div class='" + td_class + "'>" + encodeText(l) +
"</div>";
else
for (h = m.empty, c = 0; c < n; c++) b = d.insertCell(c), l = c == h ? a[f] : " ", b.className = td_class, b.innerHTML = "<div class='" + td_class + "'>" + encodeText(l) + "</div>";
else {
for (c = 0; c < n; c++) b = d.insertCell(c), b.className = td_class, b.innerHTML = "<div class='" + td_class + "'>&nbsp;</div>";
for (k in a[f]) c = a[f], l = "-" + k, h = m[l], b = d.cells[h], b.className = td_class, "object" != typeof c[k] || isArray(c[k]) ? "object" == typeof c[k] && isArray(c[k]) ? b.innerHTML = '<table style="width:100%">' + $(buildArray(c[k]), !1).html() + "</table>" : b.innerHTML =
"<div class='" + td_class + "'>" + encodeText(c[k]) + "</div>" : b.innerHTML = '<table style="width:100%">' + $(buildTable(c[k]), !1).html() + "</table>"
}
return e
}
function encodeText(a) {
return $("<div />").text(a).html()
}
function isArray(a) {
return "[object Array]" === Object.prototype.toString.call(a)
}
function isEven(a) {
return 0 == a % 2
}
</script>`;
// In case you only want a specific property, change it here.
//
// // Default: You can set the entire JSON response using pm.response.json()
let tableProps = {
json: res.getBody()
};
// pm.visualizer.set(template, tableProps);
bundled: false
extensions:
ignore:
- node_modules
- .git