Add acme_clients to mirage and client count utils (#26232)

* remove yarn script that no longer works in yarn 3

* delete other deprecated yarn script

* add acme_clients to mirage handler and utils

* consolidate client names

* revert changes to homogenizeClientNaming, wait until confirmation from backend

* remove flattenDataset helper

* revert deleting flattendataset method (done in separate PR)

* move response to helper file

* cleanup utils based on test changes

* add acme_clients to tests

* rename variables and add comments!

* refactor homogenizeClientNaming and rename

* move by_namespace to test helper as well

* add comments and finally delete flattenDataset

* add more comments and update response to match no mounts shape

* update test selector

* finish updates for removing clients: null from serialized response

* final comments!

* remove arrayOfCounts helper

* Update ui/tests/integration/components/clients/page/sync-test.js

Co-authored-by: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com>

---------

Co-authored-by: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com>
This commit is contained in:
claire bontempo
2024-04-03 13:21:24 -07:00
committed by GitHub
parent 262997a532
commit d8e6ed1c5b
10 changed files with 1042 additions and 1009 deletions

View File

@@ -88,7 +88,7 @@ _All of the commands below assume you're in the `ui/` directory._
[Mirage](https://miragejs.com/docs/getting-started/introduction/) can be helpful for mocking backend endpoints.
Look in [mirage/handlers](mirage/handlers/) for existing mocked backends.
Run yarn with mirage: `yarn start:mirage handlername`
Run yarn with mirage: `export MIRAGE_DEV_HANDLER=<handler> yarn start`
Where `handlername` is one of the options exported in [mirage/handlers/index](mirage/handlers/index.js)

View File

@@ -5,8 +5,11 @@
import ApplicationSerializer from '../application';
import { formatISO } from 'date-fns';
import { formatByMonths, formatByNamespace, homogenizeClientNaming } from 'core/utils/client-count-utils';
import { formatByMonths, formatByNamespace, destructureClientCounts } from 'core/utils/client-count-utils';
import timestamp from 'core/utils/timestamp';
// see tests/helpers/clients for sample API response (ACTIVITY_RESPONSE_STUB)
// and transformed by_namespace and by_month examples (SERIALIZED_ACTIVITY_RESPONSE)
export default class ActivitySerializer extends ApplicationSerializer {
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
if (payload.id === 'no-data') {
@@ -18,7 +21,7 @@ export default class ActivitySerializer extends ApplicationSerializer {
response_timestamp,
by_namespace: formatByNamespace(payload.data.by_namespace),
by_month: formatByMonths(payload.data.months),
total: homogenizeClientNaming(payload.data.total),
total: destructureClientCounts(payload.data.total),
};
delete payload.data.by_namespace;
delete payload.data.months;
@@ -26,47 +29,3 @@ export default class ActivitySerializer extends ApplicationSerializer {
return super.normalizeResponse(store, primaryModelClass, transformedPayload, id, requestType);
}
}
/*
SAMPLE PAYLOAD BEFORE/AFTER:
payload.data.by_namespace = [
{
namespace_id: '5SWT8',
namespace_path: 'namespacelonglonglong4/',
counts: {
entity_clients: 171,
non_entity_clients: 20,
clients: 191,
},
mounts: [
{
mount_path: 'auth/method/uMGBU',
"counts":{
"distinct_entities":0,
"entity_clients":0,
"non_entity_tokens":0,
"non_entity_clients":10,
"clients":10
}
},
],
},
];
transformedPayload.by_namespace = [
{
label: 'namespacelonglonglong4/',
entity_clients: 171,
non_entity_clients: 20,
clients: 191,
mounts: [
{
label: 'auth/method/uMGBU',
entity_clients: 20,
non_entity_clients: 15,
clients: 35,
},
],
},
]
*/

View File

@@ -6,6 +6,15 @@
import { parseAPITimestamp } from 'core/utils/date-formatters';
import { compareAsc, getUnixTime, isWithinInterval } from 'date-fns';
// add new types here
export const CLIENT_TYPES = [
'acme_clients',
'clients', // summation of total clients
'entity_clients',
'non_entity_clients',
'secret_syncs',
];
// returns array of VersionHistoryModels for noteworthy upgrades: 1.9, 1.10
// that occurred between timestamps (i.e. queried activity data)
export const filterVersionHistory = (versionHistory, start, end) => {
@@ -54,87 +63,57 @@ export const formatByMonths = (monthsArray) => {
const month = parseAPITimestamp(m.timestamp, 'M/yy');
const totalClientsByNamespace = formatByNamespace(m.namespaces);
const newClientsByNamespace = formatByNamespace(m.new_clients?.namespaces);
if (Object.keys(m).includes('counts')) {
const totalCounts = flattenDataset(m);
const newCounts = m.new_clients ? flattenDataset(m.new_clients) : {};
return {
return {
month,
timestamp: m.timestamp,
...destructureClientCounts(m?.counts),
namespaces: formatByNamespace(m.namespaces) || [],
namespaces_by_key: namespaceArrayToObject(
totalClientsByNamespace,
newClientsByNamespace,
month,
m.timestamp
),
new_clients: {
month,
timestamp: m.timestamp,
...totalCounts,
namespaces: formatByNamespace(m.namespaces) || [],
namespaces_by_key: namespaceArrayToObject(
totalClientsByNamespace,
newClientsByNamespace,
month,
m.timestamp
),
new_clients: {
month,
timestamp: m.timestamp,
...newCounts,
namespaces: formatByNamespace(m.new_clients?.namespaces) || [],
},
};
}
...destructureClientCounts(m?.new_clients?.counts),
namespaces: formatByNamespace(m.new_clients?.namespaces) || [],
},
};
});
};
export const formatByNamespace = (namespaceArray) => {
if (!Array.isArray(namespaceArray)) return namespaceArray;
return namespaceArray?.map((ns) => {
// 'namespace_path' is an empty string for root
if (ns['namespace_id'] === 'root') ns['namespace_path'] = 'root';
const label = ns['namespace_path'];
const flattenedNs = flattenDataset(ns);
// if no mounts, mounts will be an empty array
flattenedNs.mounts = [];
if (ns?.mounts && ns.mounts.length > 0) {
flattenedNs.mounts = ns.mounts.map((mount) => {
return {
label: mount['mount_path'],
...flattenDataset(mount),
};
});
// i.e. 'namespace_path' is an empty string for 'root', so use namespace_id
const label = ns.namespace_path === '' ? ns.namespace_id : ns.namespace_path;
// data prior to adding mount granularity will still have a mounts key,
// but with the value: "no mount accessor (pre-1.10 upgrade?)" (ref: vault/activity_log_util_common.go)
// transform to an empty array for type consistency
let mounts = [];
if (Array.isArray(ns.mounts)) {
mounts = ns.mounts.map((m) => ({ label: m['mount_path'], ...destructureClientCounts(m?.counts) }));
}
return {
label,
...flattenedNs,
...destructureClientCounts(ns.counts),
mounts,
};
});
};
// In 1.10 'distinct_entities' changed to 'entity_clients' and
// 'non_entity_tokens' to 'non_entity_clients'
export const homogenizeClientNaming = (object) => {
// if new key names exist, only return those key/value pairs
if (Object.keys(object).includes('entity_clients')) {
const { clients, entity_clients, non_entity_clients, secret_syncs } = object;
return {
clients,
entity_clients,
non_entity_clients,
secret_syncs,
};
}
// if object only has outdated key names, update naming
if (Object.keys(object).includes('distinct_entities')) {
const { clients, distinct_entities, non_entity_tokens } = object;
return {
clients,
entity_clients: distinct_entities,
non_entity_clients: non_entity_tokens,
};
}
return object;
};
export const flattenDataset = (object) => {
if (object?.counts) {
const flattenedObject = {};
Object.keys(object['counts']).forEach((key) => (flattenedObject[key] = object['counts'][key]));
return homogenizeClientNaming(flattenedObject);
}
return object;
// In 1.10 'distinct_entities' changed to 'entity_clients' and 'non_entity_tokens' to 'non_entity_clients'
// these deprecated keys still exist on the response, so only return relevant keys here
// when querying historical data the response will always contain the latest client type keys because the activity log is
// constructed based on the version of Vault the user is on (key values will be 0)
export const destructureClientCounts = (verboseObject) => {
if (!verboseObject) return;
return CLIENT_TYPES.reduce((newObj, clientType) => {
newObj[clientType] = verboseObject[clientType];
return newObj;
}, {});
};
export const sortMonthsByTimestamp = (monthsArray) => {
@@ -152,7 +131,6 @@ export const namespaceArrayToObject = (totalClientsByNamespace, newClientsByName
const nestNewClientsWithinNamespace = totalClientsByNamespace?.map((ns) => {
const newNamespaceCounts = newClientsByNamespace?.find((n) => n.label === ns.label);
if (newNamespaceCounts) {
const { label, clients, entity_clients, non_entity_clients, secret_syncs } = newNamespaceCounts;
const newClientsByMount = [...newNamespaceCounts.mounts];
const nestNewClientsWithinMounts = ns.mounts?.map((mount) => {
const new_clients = newClientsByMount?.find((m) => m.label === mount.label) || {};
@@ -164,11 +142,8 @@ export const namespaceArrayToObject = (totalClientsByNamespace, newClientsByName
return {
...ns,
new_clients: {
label,
clients,
entity_clients,
non_entity_clients,
secret_syncs,
label: ns.label,
...destructureClientCounts(newNamespaceCounts),
mounts: newClientsByMount,
},
mounts: [...nestNewClientsWithinMounts],
@@ -193,14 +168,11 @@ export const namespaceArrayToObject = (totalClientsByNamespace, newClientsByName
};
});
const { label, clients, entity_clients, non_entity_clients, secret_syncs, new_clients } = namespaceObject;
const { label, new_clients } = namespaceObject;
namespaces_by_key[label] = {
month,
timestamp,
clients,
entity_clients,
non_entity_clients,
secret_syncs,
...destructureClientCounts(namespaceObject),
new_clients: { month, ...new_clients },
mounts_by_key,
};
@@ -239,70 +211,3 @@ export const namespaceArrayToObject = (totalClientsByNamespace, newClientsByName
};
*/
};
/*
API RESPONSE STRUCTURE:
data: {
** by_namespace organized in descending order of client count number **
by_namespace: [
{
namespace_id: '96OwG',
namespace_path: 'test-ns/',
counts: {},
mounts: [{ mount_path: 'path-1', counts: {} }],
},
],
** months organized in ascending order of timestamps, oldest to most recent
months: [
{
timestamp: '2022-03-01T00:00:00Z',
counts: {},
namespaces: [
{
namespace_id: 'root',
namespace_path: '',
counts: {},
mounts: [{ mount_path: 'auth/up2/', counts: {} }],
},
],
new_clients: {
counts: {},
namespaces: [
{
namespace_id: 'root',
namespace_path: '',
counts: {},
mounts: [{ mount_path: 'auth/up2/', counts: {} }],
},
],
},
},
{
timestamp: '2022-04-01T00:00:00Z',
counts: {},
namespaces: [
{
namespace_id: 'root',
namespace_path: '',
counts: {},
mounts: [{ mount_path: 'auth/up2/', counts: {} }],
},
],
new_clients: {
counts: {},
namespaces: [
{
namespace_id: 'root',
namespace_path: '',
counts: {},
mounts: [{ mount_path: 'auth/up2/', counts: {} }],
},
],
},
},
],
start_time: 'start timestamp string',
end_time: 'end timestamp string',
total: { clients: 300, non_entity_clients: 100, entity_clients: 400} ,
}
*/

View File

@@ -17,7 +17,14 @@ import {
subMonths,
} from 'date-fns';
import { parseAPITimestamp } from 'core/utils/date-formatters';
import { CLIENT_TYPES } from 'core/utils/client-count-utils';
/*
HOW TO ADD NEW TYPES:
1. add key to CLIENT_TYPES
2. Find "ADD NEW CLIENT TYPES HERE" comment below and add type to destructuring array
3. Add generateMounts() for that client type to the mounts array
*/
export const LICENSE_START = new Date('2023-07-02T00:00:00Z');
export const STATIC_NOW = new Date('2024-01-25T23:59:59Z');
const COUNTS_START = subMonths(STATIC_NOW, 12); // user started Vault cluster on 2023-01-25
@@ -29,13 +36,16 @@ function getSum(array, key) {
}
function getTotalCounts(array) {
const counts = CLIENT_TYPES.reduce((obj, key) => {
obj[key] = getSum(array, key);
return obj;
}, {});
// add deprecated keys
return {
distinct_entities: getSum(array, 'entity_clients'),
entity_clients: getSum(array, 'entity_clients'),
non_entity_tokens: getSum(array, 'non_entity_clients'),
non_entity_clients: getSum(array, 'non_entity_clients'),
secret_syncs: getSum(array, 'secret_syncs'),
clients: getSum(array, 'clients'),
...counts,
distinct_entities: counts.entity_clients,
non_entity_tokens: counts.non_entity_clients,
};
}
@@ -43,15 +53,23 @@ function randomBetween(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
function arrayOfCounts(max, arrayLength) {
var result = [];
var sum = 0;
for (var i = 0; i < arrayLength - 1; i++) {
result[i] = randomBetween(1, max - (arrayLength - i - 1) - sum);
sum += result[i];
}
result[arrayLength - 1] = max - sum;
return result.sort((a, b) => b - a);
function generateMounts(pathPrefix, counts) {
const baseObject = CLIENT_TYPES.reduce((obj, key) => {
obj[key] = 0;
return obj;
}, {});
return Array.from(Array(5)).map((mount, index) => {
return {
mount_path: `${pathPrefix}${index}`,
counts: {
...baseObject,
distinct_entities: 0,
non_entity_tokens: 0,
// object contains keys for which 0-values of base object to overwrite
...counts,
},
};
});
}
function generateNamespaceBlock(idx = 0, isLowerCounts = false, ns) {
@@ -63,41 +81,23 @@ function generateNamespaceBlock(idx = 0, isLowerCounts = false, ns) {
counts: {},
mounts: {},
};
const mounts = [];
Array.from(Array(5)).forEach((mount, index) => {
const [secretSyncs] = arrayOfCounts(randomBetween(min, max), 1);
mounts.push({
mount_path: `kvv2-engine-${index}`,
counts: {
clients: secretSyncs,
// TODO test with live backend to confirm entity keys are present (and 0) for kv mounts
entity_clients: 0,
non_entity_clients: 0,
distinct_entities: 0,
non_entity_tokens: 0,
secret_syncs: secretSyncs,
},
});
});
// * ADD NEW CLIENT TYPES HERE and pass to a new generateMounts() function below
const [acme_clients, entity_clients, non_entity_clients, secret_syncs] = CLIENT_TYPES.map(() =>
randomBetween(min, max)
);
// each mount type generates a different type of client
const mounts = [
...generateMounts('auth/authid/', {
clients: non_entity_clients + entity_clients,
non_entity_clients,
entity_clients,
}),
...generateMounts('kvv2-engine-', { clients: secret_syncs, secret_syncs }),
...generateMounts('pki-engine-', { clients: acme_clients, acme_clients }),
];
// generate auth mounts array
Array.from(Array(10)).forEach((mount, index) => {
const mountClients = randomBetween(min, max);
const [nonEntity, entity] = arrayOfCounts(mountClients, 2);
mounts.push({
mount_path: `auth/authid${index}`,
counts: {
clients: mountClients,
entity_clients: entity,
non_entity_clients: nonEntity,
distinct_entities: entity,
non_entity_tokens: nonEntity,
// TODO test with live backend to confirm this key is present (and 0) for auth mounts (non-kv mounts)
secret_syncs: 0,
},
});
});
mounts.sort((a, b) => b.counts.clients - a.counts.clients);
nsBlock.mounts = mounts;
nsBlock.counts = getTotalCounts(mounts);

View File

@@ -27,14 +27,12 @@
"start": "VAULT_ADDR=http://127.0.0.1:8200; ember server --proxy=$VAULT_ADDR",
"start2": "ember server --proxy=http://127.0.0.1:8202 --port=4202",
"start:chroot": "ember server --proxy=http://127.0.0.1:8300 --port=4300",
"start:mirage": "start () { MIRAGE_DEV_HANDLER=$1 yarn run start; }; start",
"test": "concurrently --kill-others-on-fail -P -c \"auto\" -n lint:js,lint:hbs,vault \"yarn:lint:js:quiet\" \"yarn:lint:hbs:quiet\" \"node scripts/start-vault.js {@}\" --",
"test:enos": "concurrently --kill-others-on-fail -P -c \"auto\" -n lint:js,lint:hbs,enos \"yarn:lint:js:quiet\" \"yarn:lint:hbs:quiet\" \"node scripts/enos-test-ember.js {@}\" --",
"test:oss": "yarn run test -f='!enterprise' --split=8 --preserve-test-name --parallel",
"test:quick": "node scripts/start-vault.js --split=8 --preserve-test-name --parallel",
"test:quick-oss": "node scripts/start-vault.js -f='!enterprise' --split=8 --preserve-test-name --parallel",
"test:filter": "node scripts/start-vault.js --server -f='!enterprise'",
"types:declare": "declare () { yarn tsc $1 --declaration --allowJs --emitDeclarationOnly --experimentalDecorators --outDir $2; }; declare",
"vault": "VAULT_REDIRECT_ADDR=http://127.0.0.1:8200 vault server -log-level=error -dev -dev-root-token-id=root -dev-ha -dev-transactional",
"vault:cluster": "VAULT_REDIRECT_ADDR=http://127.0.0.1:8202 vault server -log-level=error -dev -dev-root-token-id=root -dev-listen-address=127.0.0.1:8202 -dev-ha -dev-transactional"
},

View File

@@ -7,6 +7,9 @@ import { Response } from 'miragejs';
import { SELECTORS as GENERAL } from 'vault/tests/helpers/general-selectors';
import { click } from '@ember/test-helpers';
import { LICENSE_START } from 'vault/mirage/handlers/clients';
import { addMonths } from 'date-fns';
/** Scenarios
Config off, no data
Config on, no data
@@ -132,3 +135,841 @@ export async function dateDropdownSelect(month, year) {
await click(dateDropdown.selectYear(year));
await click(dateDropdown.submit);
}
export const ACTIVITY_RESPONSE_STUB = {
start_time: '2023-08-01T00:00:00Z',
end_time: '2023-09-30T23:59:59Z', // is always the last day and hour of the month queried
by_namespace: [
{
namespace_id: 'root',
namespace_path: '',
counts: {
distinct_entities: 1033,
entity_clients: 1033,
non_entity_tokens: 1924,
non_entity_clients: 1924,
secret_syncs: 2397,
acme_clients: 75,
clients: 5429,
},
mounts: [
{
mount_path: 'auth/authid0',
counts: {
clients: 2957,
entity_clients: 1033,
non_entity_clients: 1924,
distinct_entities: 1033,
non_entity_tokens: 1924,
secret_syncs: 0,
acme_clients: 0,
},
},
{
mount_path: 'kvv2-engine-0',
counts: {
clients: 2397,
entity_clients: 0,
non_entity_clients: 0,
distinct_entities: 0,
non_entity_tokens: 0,
secret_syncs: 2397,
acme_clients: 0,
},
},
{
mount_path: 'pki-engine-0',
counts: {
clients: 75,
entity_clients: 0,
non_entity_clients: 0,
distinct_entities: 0,
non_entity_tokens: 0,
secret_syncs: 0,
acme_clients: 75,
},
},
],
},
{
namespace_id: '81ry61',
namespace_path: 'ns/1',
counts: {
distinct_entities: 783,
entity_clients: 783,
non_entity_tokens: 1193,
non_entity_clients: 1193,
secret_syncs: 275,
acme_clients: 125,
clients: 2376,
},
mounts: [
{
mount_path: 'auth/authid0',
counts: {
clients: 1976,
entity_clients: 783,
non_entity_clients: 1193,
distinct_entities: 783,
non_entity_tokens: 1193,
secret_syncs: 0,
acme_clients: 0,
},
},
{
mount_path: 'kvv2-engine-0',
counts: {
clients: 275,
entity_clients: 0,
non_entity_clients: 0,
distinct_entities: 0,
non_entity_tokens: 0,
secret_syncs: 275,
acme_clients: 0,
},
},
{
mount_path: 'pki-engine-0',
counts: {
clients: 125,
entity_clients: 0,
non_entity_clients: 0,
distinct_entities: 0,
non_entity_tokens: 0,
secret_syncs: 0,
acme_clients: 125,
},
},
],
},
],
months: [
{
timestamp: '2023-08-01T00:00:00Z',
counts: null,
namespaces: null,
new_clients: null,
},
{
timestamp: '2023-09-01T00:00:00Z',
counts: {
distinct_entities: 1329,
entity_clients: 1329,
non_entity_tokens: 1738,
non_entity_clients: 1738,
secret_syncs: 5525,
acme_clients: 200,
clients: 8792,
},
namespaces: [
{
namespace_id: 'root',
namespace_path: '',
counts: {
distinct_entities: 1279,
entity_clients: 1279,
non_entity_tokens: 1598,
non_entity_clients: 1598,
secret_syncs: 2755,
acme_clients: 75,
clients: 5707,
},
mounts: [
{
mount_path: 'auth/authid0',
counts: {
clients: 2877,
entity_clients: 1279,
non_entity_clients: 1598,
distinct_entities: 1279,
non_entity_tokens: 1598,
secret_syncs: 0,
acme_clients: 0,
},
},
{
mount_path: 'kvv2-engine-0',
counts: {
clients: 2755,
entity_clients: 0,
non_entity_clients: 0,
distinct_entities: 0,
non_entity_tokens: 0,
secret_syncs: 2755,
acme_clients: 0,
},
},
{
mount_path: 'pki-engine-0',
counts: {
clients: 75,
entity_clients: 0,
non_entity_clients: 0,
distinct_entities: 0,
non_entity_tokens: 0,
secret_syncs: 0,
acme_clients: 75,
},
},
],
},
{
namespace_id: '81ry61',
namespace_path: 'ns/1',
counts: {
distinct_entities: 50,
entity_clients: 50,
non_entity_tokens: 140,
non_entity_clients: 140,
secret_syncs: 2770,
acme_clients: 125,
clients: 3085,
},
mounts: [
{
mount_path: 'kvv2-engine-0',
counts: {
clients: 2770,
entity_clients: 0,
non_entity_clients: 0,
distinct_entities: 0,
non_entity_tokens: 0,
secret_syncs: 2770,
acme_clients: 0,
},
},
{
mount_path: 'auth/authid0',
counts: {
clients: 190,
entity_clients: 50,
non_entity_clients: 140,
distinct_entities: 50,
non_entity_tokens: 140,
secret_syncs: 0,
acme_clients: 0,
},
},
{
mount_path: 'pki-engine-0',
counts: {
clients: 125,
entity_clients: 0,
non_entity_clients: 0,
distinct_entities: 0,
non_entity_tokens: 0,
secret_syncs: 0,
acme_clients: 125,
},
},
],
},
],
new_clients: {
counts: {
distinct_entities: 39,
entity_clients: 39,
non_entity_tokens: 81,
non_entity_clients: 81,
secret_syncs: 166,
acme_clients: 50,
clients: 336,
},
namespaces: [
{
namespace_id: '81ry61',
namespace_path: 'ns/1',
counts: {
distinct_entities: 30,
entity_clients: 30,
non_entity_tokens: 62,
non_entity_clients: 62,
secret_syncs: 100,
acme_clients: 30,
clients: 222,
},
mounts: [
{
mount_path: 'kvv2-engine-0',
counts: {
clients: 100,
entity_clients: 0,
non_entity_clients: 0,
distinct_entities: 0,
non_entity_tokens: 0,
secret_syncs: 100,
acme_clients: 0,
},
},
{
mount_path: 'auth/authid0',
counts: {
clients: 92,
entity_clients: 30,
non_entity_clients: 62,
distinct_entities: 30,
non_entity_tokens: 62,
secret_syncs: 0,
acme_clients: 0,
},
},
{
mount_path: 'pki-engine-0',
counts: {
clients: 30,
entity_clients: 0,
non_entity_clients: 0,
distinct_entities: 0,
non_entity_tokens: 0,
secret_syncs: 0,
acme_clients: 30,
},
},
],
},
{
namespace_id: 'root',
namespace_path: '',
counts: {
distinct_entities: 9,
entity_clients: 9,
non_entity_tokens: 19,
non_entity_clients: 19,
secret_syncs: 66,
acme_clients: 20,
clients: 114,
},
mounts: [
{
mount_path: 'kvv2-engine-0',
counts: {
clients: 66,
entity_clients: 0,
non_entity_clients: 0,
distinct_entities: 0,
non_entity_tokens: 0,
secret_syncs: 66,
acme_clients: 0,
},
},
{
mount_path: 'auth/authid0',
counts: {
clients: 28,
entity_clients: 9,
non_entity_clients: 19,
distinct_entities: 9,
non_entity_tokens: 19,
secret_syncs: 0,
acme_clients: 0,
},
},
{
mount_path: 'pki-engine-0',
counts: {
clients: 20,
entity_clients: 0,
non_entity_clients: 0,
distinct_entities: 0,
non_entity_tokens: 0,
secret_syncs: 0,
acme_clients: 20,
},
},
],
},
],
},
},
],
total: {
distinct_entities: 1816,
entity_clients: 1816,
non_entity_tokens: 3117,
non_entity_clients: 3117,
secret_syncs: 2672,
acme_clients: 200,
clients: 7805,
},
};
// format returned by model hook in routes/vault/cluster/clients.ts
export const VERSION_HISTORY = [
{
version: '1.9.0',
previousVersion: null,
timestampInstalled: LICENSE_START.toISOString(),
},
{
version: '1.9.1',
previousVersion: '1.9.0',
timestampInstalled: addMonths(LICENSE_START, 1).toISOString(),
},
{
version: '1.10.1',
previousVersion: '1.9.1',
timestampInstalled: addMonths(LICENSE_START, 2).toISOString(),
},
{
version: '1.14.4',
previousVersion: '1.10.1',
timestampInstalled: addMonths(LICENSE_START, 3).toISOString(),
},
{
version: '1.16.0',
previousVersion: '1.14.4',
timestampInstalled: addMonths(LICENSE_START, 4).toISOString(),
},
];
// order of this array matters because index 0 is a month without data
export const SERIALIZED_ACTIVITY_RESPONSE = {
by_namespace: [
{
label: 'root',
clients: 5429,
entity_clients: 1033,
non_entity_clients: 1924,
secret_syncs: 2397,
acme_clients: 75,
mounts: [
{
acme_clients: 0,
clients: 2957,
entity_clients: 1033,
label: 'auth/authid0',
non_entity_clients: 1924,
secret_syncs: 0,
},
{
acme_clients: 0,
clients: 2397,
entity_clients: 0,
label: 'kvv2-engine-0',
non_entity_clients: 0,
secret_syncs: 2397,
},
{
acme_clients: 75,
clients: 75,
entity_clients: 0,
label: 'pki-engine-0',
non_entity_clients: 0,
secret_syncs: 0,
},
],
},
{
label: 'ns/1',
clients: 2376,
entity_clients: 783,
non_entity_clients: 1193,
secret_syncs: 275,
acme_clients: 125,
mounts: [
{
label: 'auth/authid0',
clients: 1976,
entity_clients: 783,
non_entity_clients: 1193,
secret_syncs: 0,
acme_clients: 0,
},
{
label: 'kvv2-engine-0',
clients: 275,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 275,
acme_clients: 0,
},
{
label: 'pki-engine-0',
acme_clients: 125,
clients: 125,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 0,
},
],
},
],
by_month: [
{
month: '8/23',
timestamp: '2023-08-01T00:00:00Z',
namespaces: [],
new_clients: {
month: '8/23',
timestamp: '2023-08-01T00:00:00Z',
namespaces: [],
},
namespaces_by_key: {},
},
{
month: '9/23',
timestamp: '2023-09-01T00:00:00Z',
clients: 8592,
entity_clients: 1329,
non_entity_clients: 1738,
secret_syncs: 5525,
namespaces: [
{
label: 'root',
clients: 5707,
entity_clients: 1279,
non_entity_clients: 1598,
secret_syncs: 2755,
acme_clients: 75,
mounts: [
{
label: 'auth/authid0',
clients: 2877,
entity_clients: 1279,
non_entity_clients: 1598,
secret_syncs: 0,
acme_clients: 0,
},
{
label: 'kvv2-engine-0',
clients: 2755,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 2755,
acme_clients: 0,
},
{
label: 'pki-engine-0',
acme_clients: 75,
clients: 75,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 0,
},
],
},
{
label: 'ns/1',
clients: 3085,
entity_clients: 50,
non_entity_clients: 140,
secret_syncs: 2770,
acme_clients: 125,
mounts: [
{
label: 'kvv2-engine-0',
clients: 2770,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 2770,
acme_clients: 0,
},
{
label: 'auth/authid0',
clients: 190,
entity_clients: 50,
non_entity_clients: 140,
secret_syncs: 0,
acme_clients: 0,
},
{
label: 'pki-engine-0',
acme_clients: 125,
clients: 125,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 0,
},
],
},
],
namespaces_by_key: {
root: {
month: '9/23',
timestamp: '2023-09-01T00:00:00Z',
clients: 5707,
entity_clients: 1279,
non_entity_clients: 1598,
secret_syncs: 2755,
acme_clients: 75,
new_clients: {
month: '9/23',
label: 'root',
clients: 114,
entity_clients: 9,
non_entity_clients: 19,
secret_syncs: 66,
acme_clients: 20,
mounts: [
{
label: 'kvv2-engine-0',
clients: 66,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 66,
acme_clients: 0,
},
{
label: 'auth/authid0',
clients: 28,
entity_clients: 9,
non_entity_clients: 19,
secret_syncs: 0,
acme_clients: 0,
},
{
label: 'pki-engine-0',
clients: 20,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 0,
acme_clients: 20,
},
],
},
mounts_by_key: {
'auth/authid0': {
month: '9/23',
timestamp: '2023-09-01T00:00:00Z',
label: 'auth/authid0',
clients: 2877,
entity_clients: 1279,
non_entity_clients: 1598,
secret_syncs: 0,
acme_clients: 0,
new_clients: {
month: '9/23',
label: 'auth/authid0',
clients: 28,
entity_clients: 9,
non_entity_clients: 19,
secret_syncs: 0,
acme_clients: 0,
},
},
'kvv2-engine-0': {
month: '9/23',
timestamp: '2023-09-01T00:00:00Z',
label: 'kvv2-engine-0',
clients: 2755,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 2755,
acme_clients: 0,
new_clients: {
month: '9/23',
label: 'kvv2-engine-0',
clients: 66,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 66,
acme_clients: 0,
},
},
'pki-engine-0': {
month: '9/23',
timestamp: '2023-09-01T00:00:00Z',
label: 'pki-engine-0',
clients: 75,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 0,
acme_clients: 75,
new_clients: {
month: '9/23',
label: 'pki-engine-0',
acme_clients: 20,
clients: 20,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 0,
},
},
},
},
'ns/1': {
month: '9/23',
timestamp: '2023-09-01T00:00:00Z',
clients: 3085,
entity_clients: 50,
non_entity_clients: 140,
secret_syncs: 2770,
acme_clients: 125,
new_clients: {
month: '9/23',
label: 'ns/1',
clients: 222,
entity_clients: 30,
non_entity_clients: 62,
secret_syncs: 100,
acme_clients: 30,
mounts: [
{
label: 'kvv2-engine-0',
clients: 100,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 100,
acme_clients: 0,
},
{
label: 'auth/authid0',
clients: 92,
entity_clients: 30,
non_entity_clients: 62,
secret_syncs: 0,
acme_clients: 0,
},
{
label: 'pki-engine-0',
acme_clients: 30,
clients: 30,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 0,
},
],
},
mounts_by_key: {
'kvv2-engine-0': {
month: '9/23',
timestamp: '2023-09-01T00:00:00Z',
label: 'kvv2-engine-0',
clients: 2770,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 2770,
acme_clients: 0,
new_clients: {
month: '9/23',
label: 'kvv2-engine-0',
clients: 100,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 100,
acme_clients: 0,
},
},
'auth/authid0': {
month: '9/23',
timestamp: '2023-09-01T00:00:00Z',
label: 'auth/authid0',
clients: 190,
entity_clients: 50,
non_entity_clients: 140,
secret_syncs: 0,
acme_clients: 0,
new_clients: {
month: '9/23',
label: 'auth/authid0',
clients: 92,
entity_clients: 30,
non_entity_clients: 62,
secret_syncs: 0,
acme_clients: 0,
},
},
'pki-engine-0': {
month: '9/23',
timestamp: '2023-09-01T00:00:00Z',
clients: 125,
acme_clients: 125,
entity_clients: 0,
label: 'pki-engine-0',
non_entity_clients: 0,
secret_syncs: 0,
new_clients: {
acme_clients: 30,
clients: 30,
entity_clients: 0,
label: 'pki-engine-0',
month: '9/23',
non_entity_clients: 0,
secret_syncs: 0,
},
},
},
},
},
new_clients: {
month: '9/23',
timestamp: '2023-09-01T00:00:00Z',
clients: 336,
entity_clients: 39,
non_entity_clients: 81,
secret_syncs: 166,
acme_clients: 50,
namespaces: [
{
label: 'ns/1',
clients: 222,
entity_clients: 30,
non_entity_clients: 62,
secret_syncs: 100,
acme_clients: 30,
mounts: [
{
label: 'kvv2-engine-0',
clients: 100,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 100,
acme_clients: 0,
},
{
label: 'auth/authid0',
clients: 92,
entity_clients: 30,
non_entity_clients: 62,
secret_syncs: 0,
acme_clients: 0,
},
{
label: 'pki-engine-0',
clients: 30,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 0,
acme_clients: 30,
},
],
},
{
label: 'root',
clients: 114,
entity_clients: 9,
non_entity_clients: 19,
secret_syncs: 66,
acme_clients: 20,
mounts: [
{
label: 'kvv2-engine-0',
clients: 66,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 66,
acme_clients: 0,
},
{
label: 'auth/authid0',
clients: 28,
entity_clients: 9,
non_entity_clients: 19,
secret_syncs: 0,
acme_clients: 0,
},
{
label: 'pki-engine-0',
clients: 20,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 0,
acme_clients: 20,
},
],
},
],
},
},
],
};

View File

@@ -84,7 +84,11 @@ module('Integration | Component | clients | Clients::Page::Sync', function (hook
});
const dataBars = findAll(charts.dataBar).filter((b) => b.hasAttribute('height'));
assert.strictEqual(dataBars.length, this.activity.byMonth.filter((m) => m.counts !== null).length);
assert.strictEqual(
dataBars.length,
this.activity.byMonth.filter((m) => m.clients).length,
'it renders a bar for each non-zero month'
);
});
test('it should render an empty state for no monthly data', async function (assert) {

View File

@@ -89,8 +89,8 @@ module('Integration | Component | clients | Page::Token', function (hooks) {
assert
.dom(`${chart} ${ts.charts.bar.dataBar}`)
.exists(
{ count: this.activity.byMonth.filter((m) => m.counts !== null).length * 2 },
'renders correct number of data bars'
{ count: this.activity.byMonth.filter((m) => m.clients).length * 2 },
'renders two stacked data bars of entity/non-entity clients for each month'
);
const formattedTimestamp = dateFormat([this.activity.responseTimestamp, 'MMM d yyyy, h:mm:ss aaa'], {
withTimeZone: true,
@@ -127,8 +127,8 @@ module('Integration | Component | clients | Page::Token', function (hooks) {
assert
.dom(`${chart} ${ts.charts.bar.dataBar}`)
.exists(
{ count: this.activity.byMonth.filter((m) => m.counts !== null).length * 2 },
'renders correct number of data bars'
{ count: this.activity.byMonth.filter((m) => m.clients).length * 2 },
'renders two stacked bars of new entity/non-entity clients for each month'
);
const formattedTimestamp = dateFormat([this.activity.responseTimestamp, 'MMM d yyyy, h:mm:ss aaa'], {
withTimeZone: true,

View File

@@ -104,7 +104,7 @@ module('Integration | Component | clients/running-total', function (hooks) {
assert
.dom(ts.charts.line.plotPoint)
.exists(
{ count: this.byMonthActivity.filter((m) => m.counts !== null).length },
{ count: this.byMonthActivity.filter((m) => m.clients).length },
'renders correct number of plot points'
);
});

View File

@@ -9,311 +9,30 @@ import {
filterVersionHistory,
formatByMonths,
formatByNamespace,
homogenizeClientNaming,
destructureClientCounts,
namespaceArrayToObject,
sortMonthsByTimestamp,
} from 'core/utils/client-count-utils';
import { LICENSE_START } from 'vault/mirage/handlers/clients';
import { addMonths } from 'date-fns';
const RESPONSE = {
start_time: '2023-08-01T00:00:00.000Z',
end_time: '2023-09-30T00:00:00.000Z',
by_namespace: [
{
namespace_id: 'root',
namespace_path: '',
counts: {
distinct_entities: 1033,
entity_clients: 1033,
non_entity_tokens: 1924,
non_entity_clients: 1924,
secret_syncs: 2397,
clients: 5354,
},
mounts: [
{
mount_path: 'auth/authid0',
counts: {
clients: 2957,
entity_clients: 1033,
non_entity_clients: 1924,
distinct_entities: 1033,
non_entity_tokens: 1924,
secret_syncs: 0,
},
},
{
mount_path: 'kvv2-engine-0',
counts: {
clients: 2397,
entity_clients: 0,
non_entity_clients: 0,
distinct_entities: 0,
non_entity_tokens: 0,
secret_syncs: 2397,
},
},
],
},
{
namespace_id: '81ry61',
namespace_path: 'ns/1',
counts: {
distinct_entities: 783,
entity_clients: 783,
non_entity_tokens: 1193,
non_entity_clients: 1193,
secret_syncs: 275,
clients: 2251,
},
mounts: [
{
mount_path: 'auth/authid0',
counts: {
clients: 1976,
entity_clients: 783,
non_entity_clients: 1193,
distinct_entities: 783,
non_entity_tokens: 1193,
secret_syncs: 0,
},
},
{
mount_path: 'kvv2-engine-0',
counts: {
clients: 275,
entity_clients: 0,
non_entity_clients: 0,
distinct_entities: 0,
non_entity_tokens: 0,
secret_syncs: 275,
},
},
],
},
],
months: [
{
timestamp: '2023-08-01T00:00:00-07:00',
counts: null,
namespaces: null,
new_clients: null,
},
{
timestamp: '2023-09-01T00:00:00-07:00',
counts: {
distinct_entities: 1329,
entity_clients: 1329,
non_entity_tokens: 1738,
non_entity_clients: 1738,
secret_syncs: 5525,
clients: 8592,
},
namespaces: [
{
namespace_id: 'root',
namespace_path: '',
counts: {
distinct_entities: 1279,
entity_clients: 1279,
non_entity_tokens: 1598,
non_entity_clients: 1598,
secret_syncs: 2755,
clients: 5632,
},
mounts: [
{
mount_path: 'auth/authid0',
counts: {
clients: 2877,
entity_clients: 1279,
non_entity_clients: 1598,
distinct_entities: 1279,
non_entity_tokens: 1598,
secret_syncs: 0,
},
},
{
mount_path: 'kvv2-engine-0',
counts: {
clients: 2755,
entity_clients: 0,
non_entity_clients: 0,
distinct_entities: 0,
non_entity_tokens: 0,
secret_syncs: 2755,
},
},
],
},
{
namespace_id: '81ry61',
namespace_path: 'ns/1',
counts: {
distinct_entities: 50,
entity_clients: 50,
non_entity_tokens: 140,
non_entity_clients: 140,
secret_syncs: 2770,
clients: 2960,
},
mounts: [
{
mount_path: 'kvv2-engine-0',
counts: {
clients: 2770,
entity_clients: 0,
non_entity_clients: 0,
distinct_entities: 0,
non_entity_tokens: 0,
secret_syncs: 2770,
},
},
{
mount_path: 'auth/authid0',
counts: {
clients: 190,
entity_clients: 50,
non_entity_clients: 140,
distinct_entities: 50,
non_entity_tokens: 140,
secret_syncs: 0,
},
},
],
},
],
new_clients: {
counts: {
distinct_entities: 39,
entity_clients: 39,
non_entity_tokens: 81,
non_entity_clients: 81,
secret_syncs: 166,
clients: 286,
},
namespaces: [
{
namespace_id: '81ry61',
namespace_path: 'ns/1',
counts: {
distinct_entities: 30,
entity_clients: 30,
non_entity_tokens: 62,
non_entity_clients: 62,
secret_syncs: 100,
clients: 192,
},
mounts: [
{
mount_path: 'kvv2-engine-0',
counts: {
clients: 100,
entity_clients: 0,
non_entity_clients: 0,
distinct_entities: 0,
non_entity_tokens: 0,
secret_syncs: 100,
},
},
{
mount_path: 'auth/authid0',
counts: {
clients: 92,
entity_clients: 30,
non_entity_clients: 62,
distinct_entities: 30,
non_entity_tokens: 62,
secret_syncs: 0,
},
},
],
},
{
namespace_id: 'root',
namespace_path: '',
counts: {
distinct_entities: 9,
entity_clients: 9,
non_entity_tokens: 19,
non_entity_clients: 19,
secret_syncs: 66,
clients: 94,
},
mounts: [
{
mount_path: 'kvv2-engine-0',
counts: {
clients: 66,
entity_clients: 0,
non_entity_clients: 0,
distinct_entities: 0,
non_entity_tokens: 0,
secret_syncs: 66,
},
},
{
mount_path: 'auth/authid0',
counts: {
clients: 28,
entity_clients: 9,
non_entity_clients: 19,
distinct_entities: 9,
non_entity_tokens: 19,
secret_syncs: 0,
},
},
],
},
],
},
},
],
total: {
distinct_entities: 1816,
entity_clients: 1816,
non_entity_tokens: 3117,
non_entity_clients: 3117,
secret_syncs: 2672,
clients: 7605,
},
};
import {
ACTIVITY_RESPONSE_STUB as RESPONSE,
VERSION_HISTORY,
SERIALIZED_ACTIVITY_RESPONSE,
} from 'vault/tests/helpers/clients';
/*
formatByNamespace, formatByMonths, destructureClientCounts are utils
used to normalize the sys/counters/activity response in the clients/activity
serializer. these functions are tested individually here, instead of all at once
in a serializer test for easier debugging
*/
module('Integration | Util | client count utils', function (hooks) {
setupTest(hooks);
test('filterVersionHistory: returns version data for relevant upgrades that occurred during date range', async function (assert) {
assert.expect(2);
// LICENSE_START is '2023-07-02T00:00:00Z'
const versionHistory = [
{
version: '1.9.0',
previousVersion: null,
timestampInstalled: LICENSE_START.toISOString(),
},
{
version: '1.9.1',
previousVersion: '1.9.0',
timestampInstalled: addMonths(LICENSE_START, 1).toISOString(),
},
{
version: '1.10.1',
previousVersion: '1.9.1',
timestampInstalled: addMonths(LICENSE_START, 2).toISOString(),
},
{
version: '1.14.4',
previousVersion: '1.10.1',
timestampInstalled: addMonths(LICENSE_START, 3).toISOString(),
},
{
version: '1.16.0',
previousVersion: '1.14.4',
timestampInstalled: addMonths(LICENSE_START, 4).toISOString(),
},
];
const original = [...versionHistory];
const original = [...VERSION_HISTORY];
const expected = [
{
previousVersion: null,
@@ -330,350 +49,92 @@ module('Integration | Util | client count utils', function (hooks) {
const startTime = LICENSE_START.toISOString(); // same as license start to catch same day edge cases
const endTime = '2024-03-04T16:14:21.000Z';
assert.propEqual(
filterVersionHistory(versionHistory, startTime, endTime),
filterVersionHistory(VERSION_HISTORY, startTime, endTime),
expected,
'it only returns upgrades between given start and end times'
);
assert.propEqual(versionHistory, original, 'it does not modify original array');
assert.propEqual(VERSION_HISTORY, original, 'it does not modify original array');
});
test('formatByMonths: formats the months array', async function (assert) {
assert.expect(2);
assert.expect(4);
const original = [...RESPONSE.months];
const expected = [
{
month: '8/23',
timestamp: '2023-08-01T00:00:00-07:00',
counts: null,
namespaces: [],
new_clients: {
month: '8/23',
timestamp: '2023-08-01T00:00:00-07:00',
namespaces: [],
},
namespaces_by_key: {},
},
{
month: '9/23',
timestamp: '2023-09-01T00:00:00-07:00',
clients: 8592,
entity_clients: 1329,
non_entity_clients: 1738,
secret_syncs: 5525,
namespaces: [
{
label: 'root',
clients: 5632,
entity_clients: 1279,
non_entity_clients: 1598,
secret_syncs: 2755,
mounts: [
{
label: 'auth/authid0',
clients: 2877,
entity_clients: 1279,
non_entity_clients: 1598,
secret_syncs: 0,
},
{
label: 'kvv2-engine-0',
clients: 2755,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 2755,
},
],
},
{
label: 'ns/1',
clients: 2960,
entity_clients: 50,
non_entity_clients: 140,
secret_syncs: 2770,
mounts: [
{
label: 'kvv2-engine-0',
clients: 2770,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 2770,
},
{
label: 'auth/authid0',
clients: 190,
entity_clients: 50,
non_entity_clients: 140,
secret_syncs: 0,
},
],
},
],
namespaces_by_key: {
root: {
month: '9/23',
timestamp: '2023-09-01T00:00:00-07:00',
clients: 5632,
entity_clients: 1279,
non_entity_clients: 1598,
secret_syncs: 2755,
new_clients: {
month: '9/23',
label: 'root',
clients: 94,
entity_clients: 9,
non_entity_clients: 19,
secret_syncs: 66,
mounts: [
{
label: 'kvv2-engine-0',
clients: 66,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 66,
},
{
label: 'auth/authid0',
clients: 28,
entity_clients: 9,
non_entity_clients: 19,
secret_syncs: 0,
},
],
},
mounts_by_key: {
'auth/authid0': {
month: '9/23',
timestamp: '2023-09-01T00:00:00-07:00',
label: 'auth/authid0',
clients: 2877,
entity_clients: 1279,
non_entity_clients: 1598,
secret_syncs: 0,
new_clients: {
month: '9/23',
label: 'auth/authid0',
clients: 28,
entity_clients: 9,
non_entity_clients: 19,
secret_syncs: 0,
},
},
'kvv2-engine-0': {
month: '9/23',
timestamp: '2023-09-01T00:00:00-07:00',
label: 'kvv2-engine-0',
clients: 2755,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 2755,
new_clients: {
month: '9/23',
label: 'kvv2-engine-0',
clients: 66,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 66,
},
},
},
},
'ns/1': {
month: '9/23',
timestamp: '2023-09-01T00:00:00-07:00',
clients: 2960,
entity_clients: 50,
non_entity_clients: 140,
secret_syncs: 2770,
new_clients: {
month: '9/23',
label: 'ns/1',
clients: 192,
entity_clients: 30,
non_entity_clients: 62,
secret_syncs: 100,
mounts: [
{
label: 'kvv2-engine-0',
clients: 100,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 100,
},
{
label: 'auth/authid0',
clients: 92,
entity_clients: 30,
non_entity_clients: 62,
secret_syncs: 0,
},
],
},
mounts_by_key: {
'kvv2-engine-0': {
month: '9/23',
timestamp: '2023-09-01T00:00:00-07:00',
label: 'kvv2-engine-0',
clients: 2770,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 2770,
new_clients: {
month: '9/23',
label: 'kvv2-engine-0',
clients: 100,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 100,
},
},
'auth/authid0': {
month: '9/23',
timestamp: '2023-09-01T00:00:00-07:00',
label: 'auth/authid0',
clients: 190,
entity_clients: 50,
non_entity_clients: 140,
secret_syncs: 0,
new_clients: {
month: '9/23',
label: 'auth/authid0',
clients: 92,
entity_clients: 30,
non_entity_clients: 62,
secret_syncs: 0,
},
},
},
},
},
new_clients: {
month: '9/23',
timestamp: '2023-09-01T00:00:00-07:00',
clients: 286,
entity_clients: 39,
non_entity_clients: 81,
secret_syncs: 166,
namespaces: [
{
label: 'ns/1',
clients: 192,
entity_clients: 30,
non_entity_clients: 62,
secret_syncs: 100,
mounts: [
{
label: 'kvv2-engine-0',
clients: 100,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 100,
},
{
label: 'auth/authid0',
clients: 92,
entity_clients: 30,
non_entity_clients: 62,
secret_syncs: 0,
},
],
},
{
label: 'root',
clients: 94,
entity_clients: 9,
non_entity_clients: 19,
secret_syncs: 66,
mounts: [
{
label: 'kvv2-engine-0',
clients: 66,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 66,
},
{
label: 'auth/authid0',
clients: 28,
entity_clients: 9,
non_entity_clients: 19,
secret_syncs: 0,
},
],
},
],
},
},
];
assert.propEqual(formatByMonths(RESPONSE.months), expected);
const [formattedNoData, formattedWithActivity] = formatByMonths(RESPONSE.months);
// instead of asserting the whole expected response, broken up so tests are easier to debug
// but kept whole above to copy/paste updated response expectations in the future
const [expectedNoData, expectedWithActivity] = SERIALIZED_ACTIVITY_RESPONSE.by_month;
const { namespaces, new_clients } = expectedWithActivity;
assert.propEqual(formattedNoData, expectedNoData, 'it formats months without data');
assert.propEqual(
formattedWithActivity.namespaces,
namespaces,
'it formats namespaces array for months with data'
);
assert.propEqual(
formattedWithActivity.new_clients,
new_clients,
'it formats new_clients block for months with data'
);
assert.propEqual(RESPONSE.months, original, 'it does not modify original months array');
});
test('formatByNamespace: formats namespace arrays with and without mounts', async function (assert) {
assert.expect(2);
test('formatByNamespace: formats namespace array with mounts', async function (assert) {
assert.expect(3);
const original = [...RESPONSE.by_namespace];
const expected = [
{
clients: 5354,
entity_clients: 1033,
label: 'root',
mounts: [
{
clients: 2957,
entity_clients: 1033,
label: 'auth/authid0',
non_entity_clients: 1924,
secret_syncs: 0,
},
{
clients: 2397,
entity_clients: 0,
label: 'kvv2-engine-0',
non_entity_clients: 0,
secret_syncs: 2397,
},
],
non_entity_clients: 1924,
secret_syncs: 2397,
},
{
clients: 2251,
entity_clients: 783,
label: 'ns/1',
mounts: [
{
clients: 1976,
entity_clients: 783,
label: 'auth/authid0',
non_entity_clients: 1193,
secret_syncs: 0,
},
{
clients: 275,
entity_clients: 0,
label: 'kvv2-engine-0',
non_entity_clients: 0,
secret_syncs: 275,
},
],
non_entity_clients: 1193,
secret_syncs: 275,
},
];
assert.propEqual(formatByNamespace(RESPONSE.by_namespace), expected);
const [formattedRoot, formattedNs1] = formatByNamespace(RESPONSE.by_namespace);
const [root, ns1] = SERIALIZED_ACTIVITY_RESPONSE.by_namespace;
assert.propEqual(formattedRoot, root, 'it formats root namespace');
assert.propEqual(formattedNs1, ns1, 'it formats ns1/ namespace');
assert.propEqual(RESPONSE.by_namespace, original, 'it does not modify original by_namespace array');
});
test('homogenizeClientNaming: homogenizes key names when both old and new keys exist, or just old key names', async function (assert) {
test('formatByNamespace: formats namespace array with no mounts (activity log data < 1.10)', async function (assert) {
assert.expect(1);
const noMounts = [
{
namespace_id: 'root',
namespace_path: '',
counts: {
distinct_entities: 10,
entity_clients: 10,
non_entity_tokens: 20,
non_entity_clients: 20,
secret_syncs: 0,
acme_clients: 0,
clients: 30,
},
mounts: 'no mount accessor (pre-1.10 upgrade?)',
},
];
const expected = [
{
acme_clients: 0,
clients: 30,
entity_clients: 10,
label: 'root',
mounts: [],
non_entity_clients: 20,
secret_syncs: 0,
},
];
assert.propEqual(formatByNamespace(noMounts), expected, 'it formats namespace without mounts');
});
test('destructureClientCounts: homogenizes key names when both old and new keys exist, or just old key names', async function (assert) {
assert.expect(2);
const original = { ...RESPONSE.total };
const expected = {
entity_clients: 1816,
non_entity_clients: 3117,
secret_syncs: 2672,
clients: 7605,
acme_clients: 200,
clients: 7805,
};
assert.propEqual(homogenizeClientNaming(RESPONSE.total), expected);
assert.propEqual(destructureClientCounts(RESPONSE.total), expected);
assert.propEqual(RESPONSE.total, original, 'it does not modify original object');
});
@@ -687,158 +148,22 @@ module('Integration | Util | client count utils', function (hooks) {
assert.propEqual(RESPONSE.months, original, 'it does not modify original array');
});
test('namespaceArrayToObject: transforms data without modifying original', async function (assert) {
assert.expect(2);
test('namespaceArrayToObject: it generates namespaces_by_key without modifying original', async function (assert) {
assert.expect(3);
// month at 0-index has no data so use second month in array
const { namespaces, new_clients } = RESPONSE.months[1];
const monthNamespaces = formatByNamespace(namespaces);
const newClients = formatByNamespace(new_clients.namespaces);
const original = { ...RESPONSE.months[1] };
const byNamespaceKeyObject = namespaceArrayToObject(
monthNamespaces,
newClients,
formatByNamespace(namespaces),
formatByNamespace(new_clients.namespaces),
'9/23',
'2023-9-01T00:00:00Z'
'2023-09-01T00:00:00Z'
);
const expected = {
root: {
month: '9/23',
timestamp: '2023-9-01T00:00:00Z',
clients: 5632,
entity_clients: 1279,
non_entity_clients: 1598,
secret_syncs: 2755,
new_clients: {
month: '9/23',
label: 'root',
clients: 94,
entity_clients: 9,
non_entity_clients: 19,
secret_syncs: 66,
mounts: [
{
label: 'kvv2-engine-0',
clients: 66,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 66,
},
{
label: 'auth/authid0',
clients: 28,
entity_clients: 9,
non_entity_clients: 19,
secret_syncs: 0,
},
],
},
mounts_by_key: {
'auth/authid0': {
month: '9/23',
timestamp: '2023-9-01T00:00:00Z',
label: 'auth/authid0',
clients: 2877,
entity_clients: 1279,
non_entity_clients: 1598,
secret_syncs: 0,
new_clients: {
month: '9/23',
label: 'auth/authid0',
clients: 28,
entity_clients: 9,
non_entity_clients: 19,
secret_syncs: 0,
},
},
'kvv2-engine-0': {
month: '9/23',
timestamp: '2023-9-01T00:00:00Z',
label: 'kvv2-engine-0',
clients: 2755,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 2755,
new_clients: {
month: '9/23',
label: 'kvv2-engine-0',
clients: 66,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 66,
},
},
},
},
'ns/1': {
month: '9/23',
timestamp: '2023-9-01T00:00:00Z',
clients: 2960,
entity_clients: 50,
non_entity_clients: 140,
secret_syncs: 2770,
new_clients: {
month: '9/23',
label: 'ns/1',
clients: 192,
entity_clients: 30,
non_entity_clients: 62,
secret_syncs: 100,
mounts: [
{
label: 'kvv2-engine-0',
clients: 100,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 100,
},
{
label: 'auth/authid0',
clients: 92,
entity_clients: 30,
non_entity_clients: 62,
secret_syncs: 0,
},
],
},
mounts_by_key: {
'kvv2-engine-0': {
month: '9/23',
timestamp: '2023-9-01T00:00:00Z',
label: 'kvv2-engine-0',
clients: 2770,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 2770,
new_clients: {
month: '9/23',
label: 'kvv2-engine-0',
clients: 100,
entity_clients: 0,
non_entity_clients: 0,
secret_syncs: 100,
},
},
'auth/authid0': {
month: '9/23',
timestamp: '2023-9-01T00:00:00Z',
label: 'auth/authid0',
clients: 190,
entity_clients: 50,
non_entity_clients: 140,
secret_syncs: 0,
new_clients: {
month: '9/23',
label: 'auth/authid0',
clients: 92,
entity_clients: 30,
non_entity_clients: 62,
secret_syncs: 0,
},
},
},
},
};
assert.propEqual(
byNamespaceKeyObject,
expected,
SERIALIZED_ACTIVITY_RESPONSE.by_month[1].namespaces_by_key,
'it returns object with namespaces by key and includes mounts_by_key'
);
assert.propEqual(
@@ -846,5 +171,6 @@ module('Integration | Util | client count utils', function (hooks) {
{},
'returns an empty object when monthByNamespace = null'
);
assert.propEqual(RESPONSE.months[1], original, 'it does not modify original month data');
});
});