diff --git a/docs/index.md b/docs/index.md index ddd0fea..854c7d9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -82,6 +82,8 @@ commands: ... [TENANT] filter list for tenant id or subdomain ... --time list includes timestamps ... --skip-unchanged skip update for unchanged dependencies + ... --only-stale only update subscriptions that have not changed today + ... --only-failed only update subscriptions with UPDATE_FAILED state === cap multitenancy (cds) === ~ cdsl --cds-list [TENANT] list all cds-mtx tenant names diff --git a/docs/tenant-registry/index.md b/docs/tenant-registry/index.md index 9404d9d..ba57af6 100644 --- a/docs/tenant-registry/index.md +++ b/docs/tenant-registry/index.md @@ -48,6 +48,8 @@ Commands for this area are: ... [TENANT] filter list for tenant id or subdomain ... --time list includes timestamps ... --skip-unchanged skip update for unchanged dependencies + ... --only-stale only update subscriptions that have not changed today + ... --only-failed only update subscriptions with UPDATE_FAILED state ~ are read-only commands * are potentially _dangerous_ commands diff --git a/src/cliOptions.js b/src/cliOptions.js index ee2020d..192a1d1 100644 --- a/src/cliOptions.js +++ b/src/cliOptions.js @@ -42,6 +42,8 @@ const FLAG_ARG = Object.freeze({ USER_INFO: "--userinfo", AUTO_UNDEPLOY: "--auto-undeploy", SKIP_UNCHANGED: "--skip-unchanged", + ONLY_STALE: "--only-stale", + ONLY_FAILED: "--only-failed", }); const FORCE_FLAG = "--force"; @@ -83,6 +85,8 @@ commands: ... [TENANT] filter list for tenant id or subdomain ... --time list includes timestamps ... --skip-unchanged skip update for unchanged dependencies + ... --only-stale only update subscriptions that have not changed today + ... --only-failed only update subscriptions with UPDATE_FAILED state === cap multitenancy (cds) === ~ cdsl --cds-list [TENANT] list all cds-mtx tenant names @@ -209,13 +213,14 @@ const APP_CLI_OPTIONS = Object.freeze({ REGISTRY_LIST: { commandVariants: ["regl", "--registry-list"], optionalPassArgs: [PASS_ARG.TENANT], - optionalFlagArgs: [FLAG_ARG.TIMESTAMPS], + optionalFlagArgs: [FLAG_ARG.TIMESTAMPS, FLAG_ARG.ONLY_STALE, FLAG_ARG.ONLY_FAILED], callback: reg.registryListSubscriptions, readonly: true, }, REGISTRY_LONG_LIST: { commandVariants: ["regll", "--registry-long-list"], optionalPassArgs: [PASS_ARG.TENANT], + optionalFlagArgs: [FLAG_ARG.ONLY_STALE, FLAG_ARG.ONLY_FAILED], callback: reg.registryLongListSubscriptions, readonly: true, }, @@ -238,12 +243,13 @@ const APP_CLI_OPTIONS = Object.freeze({ }, REGISTRY_UPDATE_ALL_DEPENDENCIES: { commandVariants: ["--registry-update-all"], - optionalFlagArgs: [FLAG_ARG.SKIP_UNCHANGED], + optionalFlagArgs: [FLAG_ARG.SKIP_UNCHANGED, FLAG_ARG.ONLY_STALE, FLAG_ARG.ONLY_FAILED], callback: reg.registryUpdateAllDependencies, }, REGISTRY_UPDATE_APP_URL: { commandVariants: ["--registry-update-url"], optionalPassArgs: [PASS_ARG.TENANT_ID], + optionalFlagArgs: [FLAG_ARG.ONLY_STALE, FLAG_ARG.ONLY_FAILED], callback: reg.registryUpdateApplicationURL, }, REGISTRY_OFFBOARD_SUBSCRIPTION: { diff --git a/src/shared/static.js b/src/shared/static.js index ae12027..5ac70d1 100644 --- a/src/shared/static.js +++ b/src/shared/static.js @@ -209,7 +209,7 @@ const nextFreePort = async (port) => { } }; -const _dateDiffInDays = (from, to) => { +const dateDiffInDays = (from, to) => { const fromDate = Date.UTC(from.getFullYear(), from.getMonth(), from.getDate()); const toDate = Date.UTC(to.getFullYear(), to.getMonth(), to.getDate()); return Math.floor((toDate - fromDate) / 1000 / 60 / 60 / 24); @@ -220,7 +220,7 @@ const formatTimestampWithRelativeDays = (input, nowDate = new Date()) => { return ""; } const inputDate = new Date(input); - const daysAgo = _dateDiffInDays(inputDate, nowDate); + const daysAgo = dateDiffInDays(inputDate, nowDate); const outputAbsolute = inputDate.toISOString().replace(/\.[0-9]{3}/, ""); return `${outputAbsolute} (${daysAgo} ${daysAgo === 1 ? "day" : "days"} ago)`; }; @@ -396,6 +396,7 @@ module.exports = { compareFor, partition, spawnAsync, + dateDiffInDays, formatTimestampWithRelativeDays, formatTimestampsWithRelativeDays, resolveTenantArg, diff --git a/src/submodules/tenantRegistry.js b/src/submodules/tenantRegistry.js index da250a6..492393e 100644 --- a/src/submodules/tenantRegistry.js +++ b/src/submodules/tenantRegistry.js @@ -13,6 +13,7 @@ const { isDashedWord, sleep, tableList, + dateDiffInDays, formatTimestampsWithRelativeDays, resolveTenantArg, limiter, @@ -24,12 +25,16 @@ const { request } = require("../shared/request"); const REGISTRY_PAGE_SIZE = 200; const REGISTRY_JOB_POLL_FREQUENCY_FALLBACK = 15000; const REGISTRY_REQUEST_CONCURRENCY_FALLBACK = 10; -const TENANT_UPDATABLE_STATES = ["SUBSCRIBED", "UPDATE_FAILED"]; const JOB_STATE = Object.freeze({ STARTED: "STARTED", SUCCEEDED: "SUCCEEDED", FAILED: "FAILED", }); +const SUBSCRIPTION_STATE = Object.freeze({ + SUBSCRIBED: "SUBSCRIBED", + UPDATE_FAILED: "UPDATE_FAILED", +}); +const UPDATABLE_STATES = [SUBSCRIPTION_STATE.SUBSCRIBED, SUBSCRIPTION_STATE.UPDATE_FAILED]; const regRequestConcurrency = parseIntWithFallback( process.env[ENV.REG_CONCURRENCY], @@ -37,7 +42,7 @@ const regRequestConcurrency = parseIntWithFallback( ); const regPollFrequency = parseIntWithFallback(process.env[ENV.REG_FREQUENCY], REGISTRY_JOB_POLL_FREQUENCY_FALLBACK); -const _registrySubscriptionsPaged = async (context, tenant) => { +const _registrySubscriptionsPaged = async (context, { tenant, onlyFailed, onlyStale, onlyUpdatable } = {}) => { const { subdomain: filterSubdomain, tenantId: filterTenantId } = resolveTenantArg(tenant); filterSubdomain && assert(isDashedWord(filterSubdomain), `argument "${filterSubdomain}" is not a valid subdomain`); @@ -73,15 +78,23 @@ const _registrySubscriptionsPaged = async (context, tenant) => { } } - if (filterSubdomain) { - subscriptions = subscriptions.filter(({ subdomain }) => subdomain === filterSubdomain); - } + subscriptions = subscriptions.filter( + ({ state, subdomain, changedOn }) => + (!filterSubdomain || subdomain === filterSubdomain) && + (!onlyFailed || state === SUBSCRIPTION_STATE.UPDATE_FAILED) && + (!onlyUpdatable || UPDATABLE_STATES.includes(state)) && + (!onlyStale || dateDiffInDays(new Date(changedOn), new Date()) > 0) + ); return { subscriptions }; }; -const registryListSubscriptions = async (context, [tenant], [doTimestamps]) => { - const { subscriptions } = await _registrySubscriptionsPaged(context, tenant); +const registryListSubscriptions = async (context, [tenant], [doTimestamps, doOnlyStale, doOnlyFailed]) => { + const { subscriptions } = await _registrySubscriptionsPaged(context, { + tenant, + onlyStale: doOnlyStale, + onlyFailed: doOnlyFailed, + }); const headerRow = ["consumerTenantId", "globalAccountId", "subdomain", "plan", "state", "url"]; doTimestamps && headerRow.push("created_on", "updated_on"); const nowDate = new Date(); @@ -103,8 +116,8 @@ const registryListSubscriptions = async (context, [tenant], [doTimestamps]) => { return tableList(table, { withRowNumber: !tenant }); }; -const registryLongListSubscriptions = async (context, [tenant]) => { - const data = await _registrySubscriptionsPaged(context, tenant); +const registryLongListSubscriptions = async (context, [tenant], [doOnlyStale, doOnlyFailed]) => { + const data = await _registrySubscriptionsPaged(context, { tenant, onlyStale: doOnlyStale, onlyFailed: doOnlyFailed }); return JSON.stringify(data, null, 2); }; @@ -180,13 +193,18 @@ const _registryCallForTenant = async ( ? `/saas-manager/v1/${plan}/subscriptions/${subscriptionId}` : `/saas-manager/v1/${plan}/tenants/${tenantId}/subscriptions`; const token = await context.getCachedUaaTokenFromCredentials(credentials); - const response = await request({ - method, - url: saas_registry_url, - pathname, - ...(Object.keys(query).length !== 0 && { query }), - auth: { token }, - }); + let response; + try { + response = await request({ + method, + url: saas_registry_url, + pathname, + ...(Object.keys(query).length !== 0 && { query }), + auth: { token }, + }); + } catch (err) { + return { tenantId, state: JOB_STATE.FAILED, message: err.message }; + } if (!doJobPoll) { // NOTE: with checkStatus being true by default, the above request only returns for successful changes @@ -206,15 +224,21 @@ const _registryCall = async (context, method, tenantId, options) => { let results; if (tenantId) { assert(isUUID(tenantId), "TENANT_ID is not a uuid", tenantId); - const { subscriptions } = await _registrySubscriptionsPaged(context, tenantId); + const { subscriptions } = await _registrySubscriptionsPaged(context, { + tenant: tenantId, + }); assert(subscriptions.length >= 1, "could not find tenant %s", tenantId); results = [await _registryCallForTenant(context, subscriptions[0], method, options)]; } else { - const { subscriptions } = await _registrySubscriptionsPaged(context); - const updatableSubscriptions = subscriptions.filter(({ state }) => TENANT_UPDATABLE_STATES.includes(state)); + const { onlyStaleSubscriptions, onlyFailedSubscriptions } = options ?? {}; + const { subscriptions } = await _registrySubscriptionsPaged(context, { + onlyFailed: onlyFailedSubscriptions, + onlyStale: onlyStaleSubscriptions, + onlyUpdatable: true, + }); results = await limiter( regRequestConcurrency, - updatableSubscriptions, + subscriptions, async (subscription) => await _registryCallForTenant(context, subscription, method, options) ); } @@ -230,14 +254,20 @@ const _registryCall = async (context, method, tenantId, options) => { const registryUpdateDependencies = async (context, [tenantId], [doSkipUnchanged]) => await _registryCall(context, "PATCH", tenantId, { skipUnchangedDependencies: doSkipUnchanged }); -const registryUpdateAllDependencies = async (context, _, [doSkipUnchanged]) => - await _registryCall(context, "PATCH", undefined, { skipUnchangedDependencies: doSkipUnchanged }); +const registryUpdateAllDependencies = async (context, _, [doSkipUnchanged, doOnlyStale, doOnlyFailed]) => + await _registryCall(context, "PATCH", undefined, { + skipUnchangedDependencies: doSkipUnchanged, + onlyStaleSubscriptions: doOnlyStale, + onlyFailedSubscriptions: doOnlyFailed, + }); -const registryUpdateApplicationURL = async (context, [tenantId]) => +const registryUpdateApplicationURL = async (context, [tenantId], [doOnlyStale, doOnlyFailed]) => await _registryCall(context, "PATCH", tenantId, { updateApplicationURL: true, skipUpdatingDependencies: true, doJobPoll: false, + onlyStaleSubscriptions: doOnlyStale, + onlyFailedSubscriptions: doOnlyFailed, }); const registryOffboardSubscription = async (context, [tenantId]) => await _registryCall(context, "DELETE", tenantId); diff --git a/test/__snapshots__/tenantRegistry.test.js.snap b/test/__snapshots__/tenantRegistry.test.js.snap index 15265c2..65e86c2 100644 --- a/test/__snapshots__/tenantRegistry.test.js.snap +++ b/test/__snapshots__/tenantRegistry.test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`reg tests reg list paging 1`] = ` +exports[`reg tests reg list paging only failed 1`] = ` [ [ { @@ -39,6 +39,179 @@ exports[`reg tests reg list paging 1`] = ` ] `; +exports[`reg tests reg list paging only failed 2`] = ` +"# consumerTenantId globalAccountId subdomain plan state url +1 00000000-0000-0000-0000-000000000001 00000000-0000-0000-0000-000000000999 skyfin-001 standard UPDATE_FAILED https://skyfin-001.dev-afc-sap.cfapps.sap.hana.ondemand.com +2 00000000-0000-0000-0000-000000000002 00000000-0000-0000-0000-000000000998 skyfin-002 standard UPDATE_FAILED https://skyfin-002.dev-afc-sap.cfapps.sap.hana.ondemand.com +3 00000000-0000-0000-0000-000000000019 00000000-0000-0000-0000-000000000981 skyfin-019 standard UPDATE_FAILED https://skyfin-019.dev-afc-sap.cfapps.sap.hana.ondemand.com +4 00000000-0000-0000-0000-000000000020 00000000-0000-0000-0000-000000000980 skyfin-020 standard UPDATE_FAILED https://skyfin-020.dev-afc-sap.cfapps.sap.hana.ondemand.com" +`; + +exports[`reg tests reg list paging only stale 1`] = ` +[ + [ + { + "auth": { + "token": "token", + }, + "headers": { + "Accept": "application/json", + }, + "pathname": "/saas-manager/v1/plan/subscriptions", + "query": { + "appName": "appName", + "page": 1, + "size": 200, + }, + "url": "saas_registry_url", + }, + ], + [ + { + "auth": { + "token": "token", + }, + "headers": { + "Accept": "application/json", + }, + "pathname": "/saas-manager/v1/plan/subscriptions", + "query": { + "appName": "appName", + "page": 2, + "size": 200, + }, + "url": "saas_registry_url", + }, + ], +] +`; + +exports[`reg tests reg list paging only stale 2`] = ` +"# consumerTenantId globalAccountId subdomain plan state url +1 00000000-0000-0000-0000-000000000001 00000000-0000-0000-0000-000000000999 skyfin-001 standard UPDATE_FAILED https://skyfin-001.dev-afc-sap.cfapps.sap.hana.ondemand.com +2 00000000-0000-0000-0000-000000000002 00000000-0000-0000-0000-000000000998 skyfin-002 standard UPDATE_FAILED https://skyfin-002.dev-afc-sap.cfapps.sap.hana.ondemand.com +3 00000000-0000-0000-0000-000000000003 00000000-0000-0000-0000-000000000997 skyfin-003 standard SUBSCRIBED https://skyfin-003.dev-afc-sap.cfapps.sap.hana.ondemand.com +4 00000000-0000-0000-0000-000000000004 00000000-0000-0000-0000-000000000996 skyfin-004 standard SUBSCRIBED https://skyfin-004.dev-afc-sap.cfapps.sap.hana.ondemand.com +5 00000000-0000-0000-0000-000000000005 00000000-0000-0000-0000-000000000995 skyfin-005 standard SUBSCRIBED https://skyfin-005.dev-afc-sap.cfapps.sap.hana.ondemand.com +6 00000000-0000-0000-0000-000000000006 00000000-0000-0000-0000-000000000994 skyfin-006 standard SUBSCRIBED https://skyfin-006.dev-afc-sap.cfapps.sap.hana.ondemand.com +7 00000000-0000-0000-0000-000000000007 00000000-0000-0000-0000-000000000993 skyfin-007 standard SUBSCRIBED https://skyfin-007.dev-afc-sap.cfapps.sap.hana.ondemand.com +8 00000000-0000-0000-0000-000000000008 00000000-0000-0000-0000-000000000992 skyfin-008 standard SUBSCRIBED https://skyfin-008.dev-afc-sap.cfapps.sap.hana.ondemand.com +9 00000000-0000-0000-0000-000000000009 00000000-0000-0000-0000-000000000991 skyfin-009 standard SUBSCRIBED https://skyfin-009.dev-afc-sap.cfapps.sap.hana.ondemand.com +10 00000000-0000-0000-0000-000000000010 00000000-0000-0000-0000-000000000990 skyfin-010 standard SUBSCRIBED https://skyfin-010.dev-afc-sap.cfapps.sap.hana.ondemand.com +11 00000000-0000-0000-0000-000000000011 00000000-0000-0000-0000-000000000989 skyfin-011 standard SUBSCRIBED https://skyfin-011.dev-afc-sap.cfapps.sap.hana.ondemand.com +12 00000000-0000-0000-0000-000000000012 00000000-0000-0000-0000-000000000988 skyfin-012 standard SUBSCRIBED https://skyfin-012.dev-afc-sap.cfapps.sap.hana.ondemand.com +13 00000000-0000-0000-0000-000000000013 00000000-0000-0000-0000-000000000987 skyfin-013 standard SUBSCRIBED https://skyfin-013.dev-afc-sap.cfapps.sap.hana.ondemand.com +14 00000000-0000-0000-0000-000000000014 00000000-0000-0000-0000-000000000986 skyfin-014 standard SUBSCRIBED https://skyfin-014.dev-afc-sap.cfapps.sap.hana.ondemand.com" +`; + +exports[`reg tests reg list paging only stale and failed 1`] = ` +[ + [ + { + "auth": { + "token": "token", + }, + "headers": { + "Accept": "application/json", + }, + "pathname": "/saas-manager/v1/plan/subscriptions", + "query": { + "appName": "appName", + "page": 1, + "size": 200, + }, + "url": "saas_registry_url", + }, + ], + [ + { + "auth": { + "token": "token", + }, + "headers": { + "Accept": "application/json", + }, + "pathname": "/saas-manager/v1/plan/subscriptions", + "query": { + "appName": "appName", + "page": 2, + "size": 200, + }, + "url": "saas_registry_url", + }, + ], +] +`; + +exports[`reg tests reg list paging only stale and failed 2`] = ` +"# consumerTenantId globalAccountId subdomain plan state url +1 00000000-0000-0000-0000-000000000001 00000000-0000-0000-0000-000000000999 skyfin-001 standard UPDATE_FAILED https://skyfin-001.dev-afc-sap.cfapps.sap.hana.ondemand.com +2 00000000-0000-0000-0000-000000000002 00000000-0000-0000-0000-000000000998 skyfin-002 standard UPDATE_FAILED https://skyfin-002.dev-afc-sap.cfapps.sap.hana.ondemand.com" +`; + +exports[`reg tests reg list paging unfiltered 1`] = ` +[ + [ + { + "auth": { + "token": "token", + }, + "headers": { + "Accept": "application/json", + }, + "pathname": "/saas-manager/v1/plan/subscriptions", + "query": { + "appName": "appName", + "page": 1, + "size": 200, + }, + "url": "saas_registry_url", + }, + ], + [ + { + "auth": { + "token": "token", + }, + "headers": { + "Accept": "application/json", + }, + "pathname": "/saas-manager/v1/plan/subscriptions", + "query": { + "appName": "appName", + "page": 2, + "size": 200, + }, + "url": "saas_registry_url", + }, + ], +] +`; + +exports[`reg tests reg list paging unfiltered 2`] = ` +"# consumerTenantId globalAccountId subdomain plan state url +1 00000000-0000-0000-0000-000000000001 00000000-0000-0000-0000-000000000999 skyfin-001 standard UPDATE_FAILED https://skyfin-001.dev-afc-sap.cfapps.sap.hana.ondemand.com +2 00000000-0000-0000-0000-000000000002 00000000-0000-0000-0000-000000000998 skyfin-002 standard UPDATE_FAILED https://skyfin-002.dev-afc-sap.cfapps.sap.hana.ondemand.com +3 00000000-0000-0000-0000-000000000003 00000000-0000-0000-0000-000000000997 skyfin-003 standard SUBSCRIBED https://skyfin-003.dev-afc-sap.cfapps.sap.hana.ondemand.com +4 00000000-0000-0000-0000-000000000004 00000000-0000-0000-0000-000000000996 skyfin-004 standard SUBSCRIBED https://skyfin-004.dev-afc-sap.cfapps.sap.hana.ondemand.com +5 00000000-0000-0000-0000-000000000005 00000000-0000-0000-0000-000000000995 skyfin-005 standard SUBSCRIBED https://skyfin-005.dev-afc-sap.cfapps.sap.hana.ondemand.com +6 00000000-0000-0000-0000-000000000006 00000000-0000-0000-0000-000000000994 skyfin-006 standard SUBSCRIBED https://skyfin-006.dev-afc-sap.cfapps.sap.hana.ondemand.com +7 00000000-0000-0000-0000-000000000007 00000000-0000-0000-0000-000000000993 skyfin-007 standard SUBSCRIBED https://skyfin-007.dev-afc-sap.cfapps.sap.hana.ondemand.com +8 00000000-0000-0000-0000-000000000008 00000000-0000-0000-0000-000000000992 skyfin-008 standard SUBSCRIBED https://skyfin-008.dev-afc-sap.cfapps.sap.hana.ondemand.com +9 00000000-0000-0000-0000-000000000009 00000000-0000-0000-0000-000000000991 skyfin-009 standard SUBSCRIBED https://skyfin-009.dev-afc-sap.cfapps.sap.hana.ondemand.com +10 00000000-0000-0000-0000-000000000010 00000000-0000-0000-0000-000000000990 skyfin-010 standard SUBSCRIBED https://skyfin-010.dev-afc-sap.cfapps.sap.hana.ondemand.com +11 00000000-0000-0000-0000-000000000011 00000000-0000-0000-0000-000000000989 skyfin-011 standard SUBSCRIBED https://skyfin-011.dev-afc-sap.cfapps.sap.hana.ondemand.com +12 00000000-0000-0000-0000-000000000012 00000000-0000-0000-0000-000000000988 skyfin-012 standard SUBSCRIBED https://skyfin-012.dev-afc-sap.cfapps.sap.hana.ondemand.com +13 00000000-0000-0000-0000-000000000013 00000000-0000-0000-0000-000000000987 skyfin-013 standard SUBSCRIBED https://skyfin-013.dev-afc-sap.cfapps.sap.hana.ondemand.com +14 00000000-0000-0000-0000-000000000014 00000000-0000-0000-0000-000000000986 skyfin-014 standard SUBSCRIBED https://skyfin-014.dev-afc-sap.cfapps.sap.hana.ondemand.com +15 00000000-0000-0000-0000-000000000015 00000000-0000-0000-0000-000000000985 skyfin-015 standard SUBSCRIBED https://skyfin-015.dev-afc-sap.cfapps.sap.hana.ondemand.com +16 00000000-0000-0000-0000-000000000016 00000000-0000-0000-0000-000000000984 skyfin-016 standard SUBSCRIBED https://skyfin-016.dev-afc-sap.cfapps.sap.hana.ondemand.com +17 00000000-0000-0000-0000-000000000017 00000000-0000-0000-0000-000000000983 skyfin-017 standard SUBSCRIBED https://skyfin-017.dev-afc-sap.cfapps.sap.hana.ondemand.com +18 00000000-0000-0000-0000-000000000018 00000000-0000-0000-0000-000000000982 skyfin-018 standard SUBSCRIBED https://skyfin-018.dev-afc-sap.cfapps.sap.hana.ondemand.com +19 00000000-0000-0000-0000-000000000019 00000000-0000-0000-0000-000000000981 skyfin-019 standard UPDATE_FAILED https://skyfin-019.dev-afc-sap.cfapps.sap.hana.ondemand.com +20 00000000-0000-0000-0000-000000000020 00000000-0000-0000-0000-000000000980 skyfin-020 standard UPDATE_FAILED https://skyfin-020.dev-afc-sap.cfapps.sap.hana.ondemand.com" +`; + exports[`reg tests reg update with request failed 2`] = ` [ [ diff --git a/test/tenantRegistry.nock.test.js b/test/tenantRegistry.nock.test.js index 89ef1e2..ee2c29d 100644 --- a/test/tenantRegistry.nock.test.js +++ b/test/tenantRegistry.nock.test.js @@ -74,7 +74,7 @@ describe("reg tests", () => { GET https://saas-manager.mesh.cf.sap.hana.ondemand.com/saas-manager/v1/application/subscriptions?appName=afc-dev&size=200&page=1 200 OK (88ms)" `); loggerSpy.info.mockClear(); - expect(await reg.registryListSubscriptions(await freshContext(), [], [false])).toMatchInlineSnapshot(` + expect(await reg.registryListSubscriptions(await freshContext(), [], [])).toMatchInlineSnapshot(` "# consumerTenantId globalAccountId subdomain plan state url 1 288393a7-972c-4fa8-acfd-12299c4db374 096cea2e-77ef-498f-a588-114b33817f5d nga-dev-eu10-uofvpsx0 standard SUBSCRIBED https://nga-dev-eu10-uofvpsx0.dev-afc-sap.cfapps.sap.hana.ondemand.com 2 34d6259c-41bc-4f6b-8220-018ace187813 011b4e7a-43b5-4f63-819a-9b1e46ab23b6 afc-booster-test standard SUBSCRIBED https://afc-booster-test.dev.eu10-canary.afc.cloud.sap @@ -93,7 +93,7 @@ describe("reg tests", () => { GET https://saas-manager.mesh.cf.sap.hana.ondemand.com/saas-manager/v1/application/subscriptions?appName=afc-dev&size=200&page=1 200 OK (88ms)" `); loggerSpy.info.mockClear(); - expect(await reg.registryLongListSubscriptions(await freshContext(), [])).toMatchSnapshot(); + expect(await reg.registryLongListSubscriptions(await freshContext(), [], [])).toMatchSnapshot(); expect(outputFromLoggerPartitionFetch(loggerSpy.info.mock.calls)).toMatchInlineSnapshot(` "targeting cf api https://api.cf.sap.hana.ondemand.com / org "skyfin" / space "dev" @@ -119,7 +119,7 @@ describe("reg tests", () => { GET https://saas-manager.mesh.cf.sap.hana.ondemand.com/saas-manager/v1/application/subscriptions?appName=afc-dev&tenantId=5ecc7413-2b7e-414a-9496-ad4a61f6cccf&size=200&page=1 200 OK (88ms)" `); loggerSpy.info.mockClear(); - expect(await reg.registryListSubscriptions(await freshContext(), [testTenantId], [false])).toMatchInlineSnapshot(` + expect(await reg.registryListSubscriptions(await freshContext(), [testTenantId], [])).toMatchInlineSnapshot(` "consumerTenantId globalAccountId subdomain plan state url 5ecc7413-2b7e-414a-9496-ad4a61f6cccf 011b4e7a-43b5-4f63-819a-9b1e46ab23b6 skyfin-company standard SUBSCRIBED https://skyfin-company.dev-afc-sap.cfapps.sap.hana.ondemand.com" `); @@ -129,7 +129,7 @@ describe("reg tests", () => { GET https://saas-manager.mesh.cf.sap.hana.ondemand.com/saas-manager/v1/application/subscriptions?appName=afc-dev&tenantId=5ecc7413-2b7e-414a-9496-ad4a61f6cccf&size=200&page=1 200 OK (88ms)" `); loggerSpy.info.mockClear(); - const result = await reg.registryLongListSubscriptions(await freshContext(), [testTenantId]); + const result = await reg.registryLongListSubscriptions(await freshContext(), [testTenantId], []); expect(JSON.parse(result).subscriptions).toHaveLength(1); expect(result).toMatchSnapshot(); expect(outputFromLoggerPartitionFetch(loggerSpy.info.mock.calls)).toMatchInlineSnapshot(` @@ -170,7 +170,7 @@ describe("reg tests", () => { test("reg update tenant", async () => { const { nockDone } = await nockBack("reg-update-tenant.json", { afterRecord: anonymizeNock }); - expect(await reg.registryUpdateDependencies(await freshContext(), [testTenantId], [false])).toBeUndefined(); + expect(await reg.registryUpdateDependencies(await freshContext(), [testTenantId], [])).toBeUndefined(); expect(outputFromLoggerPartitionFetch(loggerSpy.info.mock.calls)).toMatchInlineSnapshot(` "targeting cf api https://api.cf.sap.hana.ondemand.com / org "skyfin" / space "dev" response: Job for update subscription of application: afc-dev and tenant: 5ecc7413-2b7e-414a-9496-ad4a61f6cccf, was created @@ -193,7 +193,7 @@ describe("reg tests", () => { test("reg update tenant all", async () => { const { nockDone } = await nockBack("reg-update-tenant-all.json", { afterRecord: anonymizeNock }); - expect(await reg.registryUpdateAllDependencies(await freshContext(), undefined, [false])).toBeUndefined(); + expect(await reg.registryUpdateAllDependencies(await freshContext(), undefined, [])).toBeUndefined(); expect(outputFromLoggerPartitionFetch(loggerSpy.info.mock.calls)).toMatchInlineSnapshot(` "targeting cf api https://api.cf.sap.hana.ondemand.com / org "skyfin" / space "dev" response: Job for update subscription of application: afc-dev and tenant: 5ecc7413-2b7e-414a-9496-ad4a61f6cccf, was created @@ -307,7 +307,7 @@ describe("reg tests", () => { test("reg update tenant application url all", async () => { const { nockDone } = await nockBack("reg-update-tenant-appurl-all.json", { afterRecord: anonymizeNock }); - expect(await reg.registryUpdateApplicationURL(await freshContext(), [])).toBeUndefined(); + expect(await reg.registryUpdateApplicationURL(await freshContext(), [], [])).toBeUndefined(); expect(outputFromLoggerPartitionFetch(loggerSpy.info.mock.calls)).toMatchInlineSnapshot(` "targeting cf api https://api.cf.sap.hana.ondemand.com / org "skyfin" / space "dev" [ @@ -374,7 +374,7 @@ describe("reg tests", () => { test("reg update tenant application url with tenant", async () => { const { nockDone } = await nockBack("req-update-tenant-appurl.json", { afterRecord: anonymizeNock }); - expect(await reg.registryUpdateApplicationURL(await freshContext(), [testTenantId])).toBeUndefined(); + expect(await reg.registryUpdateApplicationURL(await freshContext(), [testTenantId], [])).toBeUndefined(); expect(outputFromLoggerPartitionFetch(loggerSpy.info.mock.calls)).toMatchInlineSnapshot(` "targeting cf api https://api.cf.sap.hana.ondemand.com / org "skyfin" / space "dev" { diff --git a/test/tenantRegistry.test.js b/test/tenantRegistry.test.js index c509104..e51db91 100644 --- a/test/tenantRegistry.test.js +++ b/test/tenantRegistry.test.js @@ -15,7 +15,7 @@ const mockRequest = require("../src/shared/request"); const mockShared = require("../src/shared/static"); const reg = require("../src/submodules/tenantRegistry"); -const { anonymizeListTimestamps, outputFromLoggerPartitionFetch } = require("./util/static"); +const { outputFromLoggerPartitionFetch } = require("./util/static"); let loggerSpy = { info: jest.spyOn(console, "log").mockImplementation(), @@ -35,7 +35,7 @@ const fakeContext = { getCachedUaaTokenFromCredentials: () => "token", }; -const fakeSubscriptionFactory = (index) => { +const fakeSubscriptionFactory = (index, { doFail, doRecent } = {}) => { const paddedCount = String(index).padStart(3, "0"); const paddedAltCount = String(1000 - index).padStart(3, "0"); const uuid = `00000000-0000-0000-0000-000000000${paddedCount}`; @@ -53,9 +53,9 @@ const fakeSubscriptionFactory = (index) => { subscriptionGUID: "8999d1fa-3a3e-bbf5-c21d-2db950822b8a", code: "standard", amount: 1, - state: "SUBSCRIBED", + state: doFail ? "UPDATE_FAILED" : "SUBSCRIBED", createdOn: "Fri Mar 19 09:51:40 GMT 2021", - changedOn: "Wed Apr 03 14:49:51 GMT 2024", + changedOn: doRecent ? new Date().toUTCString() : "Wed Apr 03 14:49:51 GMT 2024", internalSubscriptionId: "afc-dev!t5874_5ecc7413-2b7e-414a-9496-ad4a61f6cccf_afc-dev!t5874", authProviderState: "SUBSCRIBED", callbackState: "UPDATE_CALLBACK_SUCCEEDED", @@ -99,8 +99,15 @@ describe("reg tests", () => { jest.clearAllMocks(); }); - test("reg list paging", async () => { - const fakeSubscriptions = Array.from({ length: 20 }).map((x, i) => fakeSubscriptionFactory(i + 1)); + test.each([ + ["unfiltered", false, false], + ["only stale", true, false], + ["only failed", false, true], + ["only stale and failed", true, true], + ])("reg list paging %s", async (_, doOnlyStale, doOnlyFail) => { + const fakeSubscriptions = Array.from({ length: 20 }).map((x, i) => + fakeSubscriptionFactory(i + 1, { doFail: i + 1 <= 2 || i + 1 >= 19, doRecent: i + 1 >= 15 }) + ); mockRequest.request.mockReturnValueOnce({ json: () => ({ subscriptions: fakeSubscriptions.slice(0, 10), morePages: true }), }); @@ -108,31 +115,9 @@ describe("reg tests", () => { json: () => ({ subscriptions: fakeSubscriptions.slice(10) }), }); - const regListOutput = await reg.registryListSubscriptions(fakeContext, [], []); + const regListOutput = await reg.registryListSubscriptions(fakeContext, [], [false, doOnlyStale, doOnlyFail]); expect(mockRequest.request.mock.calls).toMatchSnapshot(); - expect(anonymizeListTimestamps(regListOutput)).toMatchInlineSnapshot(` - "# consumerTenantId globalAccountId subdomain plan state url - 1 00000000-0000-0000-0000-000000000001 00000000-0000-0000-0000-000000000999 skyfin-001 standard SUBSCRIBED https://skyfin-001.dev-afc-sap.cfapps.sap.hana.ondemand.com - 2 00000000-0000-0000-0000-000000000002 00000000-0000-0000-0000-000000000998 skyfin-002 standard SUBSCRIBED https://skyfin-002.dev-afc-sap.cfapps.sap.hana.ondemand.com - 3 00000000-0000-0000-0000-000000000003 00000000-0000-0000-0000-000000000997 skyfin-003 standard SUBSCRIBED https://skyfin-003.dev-afc-sap.cfapps.sap.hana.ondemand.com - 4 00000000-0000-0000-0000-000000000004 00000000-0000-0000-0000-000000000996 skyfin-004 standard SUBSCRIBED https://skyfin-004.dev-afc-sap.cfapps.sap.hana.ondemand.com - 5 00000000-0000-0000-0000-000000000005 00000000-0000-0000-0000-000000000995 skyfin-005 standard SUBSCRIBED https://skyfin-005.dev-afc-sap.cfapps.sap.hana.ondemand.com - 6 00000000-0000-0000-0000-000000000006 00000000-0000-0000-0000-000000000994 skyfin-006 standard SUBSCRIBED https://skyfin-006.dev-afc-sap.cfapps.sap.hana.ondemand.com - 7 00000000-0000-0000-0000-000000000007 00000000-0000-0000-0000-000000000993 skyfin-007 standard SUBSCRIBED https://skyfin-007.dev-afc-sap.cfapps.sap.hana.ondemand.com - 8 00000000-0000-0000-0000-000000000008 00000000-0000-0000-0000-000000000992 skyfin-008 standard SUBSCRIBED https://skyfin-008.dev-afc-sap.cfapps.sap.hana.ondemand.com - 9 00000000-0000-0000-0000-000000000009 00000000-0000-0000-0000-000000000991 skyfin-009 standard SUBSCRIBED https://skyfin-009.dev-afc-sap.cfapps.sap.hana.ondemand.com - 10 00000000-0000-0000-0000-000000000010 00000000-0000-0000-0000-000000000990 skyfin-010 standard SUBSCRIBED https://skyfin-010.dev-afc-sap.cfapps.sap.hana.ondemand.com - 11 00000000-0000-0000-0000-000000000011 00000000-0000-0000-0000-000000000989 skyfin-011 standard SUBSCRIBED https://skyfin-011.dev-afc-sap.cfapps.sap.hana.ondemand.com - 12 00000000-0000-0000-0000-000000000012 00000000-0000-0000-0000-000000000988 skyfin-012 standard SUBSCRIBED https://skyfin-012.dev-afc-sap.cfapps.sap.hana.ondemand.com - 13 00000000-0000-0000-0000-000000000013 00000000-0000-0000-0000-000000000987 skyfin-013 standard SUBSCRIBED https://skyfin-013.dev-afc-sap.cfapps.sap.hana.ondemand.com - 14 00000000-0000-0000-0000-000000000014 00000000-0000-0000-0000-000000000986 skyfin-014 standard SUBSCRIBED https://skyfin-014.dev-afc-sap.cfapps.sap.hana.ondemand.com - 15 00000000-0000-0000-0000-000000000015 00000000-0000-0000-0000-000000000985 skyfin-015 standard SUBSCRIBED https://skyfin-015.dev-afc-sap.cfapps.sap.hana.ondemand.com - 16 00000000-0000-0000-0000-000000000016 00000000-0000-0000-0000-000000000984 skyfin-016 standard SUBSCRIBED https://skyfin-016.dev-afc-sap.cfapps.sap.hana.ondemand.com - 17 00000000-0000-0000-0000-000000000017 00000000-0000-0000-0000-000000000983 skyfin-017 standard SUBSCRIBED https://skyfin-017.dev-afc-sap.cfapps.sap.hana.ondemand.com - 18 00000000-0000-0000-0000-000000000018 00000000-0000-0000-0000-000000000982 skyfin-018 standard SUBSCRIBED https://skyfin-018.dev-afc-sap.cfapps.sap.hana.ondemand.com - 19 00000000-0000-0000-0000-000000000019 00000000-0000-0000-0000-000000000981 skyfin-019 standard SUBSCRIBED https://skyfin-019.dev-afc-sap.cfapps.sap.hana.ondemand.com - 20 00000000-0000-0000-0000-000000000020 00000000-0000-0000-0000-000000000980 skyfin-020 standard SUBSCRIBED https://skyfin-020.dev-afc-sap.cfapps.sap.hana.ondemand.com" - `); + expect(regListOutput).toMatchSnapshot(); expect(loggerSpy.info).toHaveBeenCalledTimes(0); expect(loggerSpy.error).toHaveBeenCalledTimes(0); }); @@ -150,7 +135,7 @@ describe("reg tests", () => { mockRequest.request.mockImplementationOnce(fakeJobImplementationFactory(index)); } - await reg.registryUpdateAllDependencies(fakeContext, undefined, [false]); + await reg.registryUpdateAllDependencies(fakeContext, undefined, []); expect(mockRequest.request).toHaveBeenCalledTimes(1 + n * 2); @@ -207,7 +192,7 @@ describe("reg tests", () => { let caughtErr; try { - await reg.registryUpdateAllDependencies(fakeContext, undefined, [false]); + await reg.registryUpdateAllDependencies(fakeContext, undefined, []); } catch (err) { caughtErr = err; } @@ -272,7 +257,7 @@ describe("reg tests", () => { let caughtErr; try { - await reg.registryUpdateAllDependencies(fakeContext, undefined, [false]); + await reg.registryUpdateAllDependencies(fakeContext, undefined, []); } catch (err) { caughtErr = err; }