diff --git a/plugins/WasabiWACM/v1/configValidation.json b/plugins/WasabiWACM/v1/configValidation.json new file mode 100644 index 00000000..0862c5a8 --- /dev/null +++ b/plugins/WasabiWACM/v1/configValidation.json @@ -0,0 +1,11 @@ +{ + "steps": [ + { + "displayName": "Authenticate", + "dataStream": { "name": "wacmConfigValidation" }, + "required": true, + "error": "Could not authenticate. Check your username and API key are correct and have not been regenerated.", + "success": "Connected successfully." + } + ] +} diff --git a/plugins/WasabiWACM/v1/custom_types.json b/plugins/WasabiWACM/v1/custom_types.json new file mode 100644 index 00000000..8dd8163c --- /dev/null +++ b/plugins/WasabiWACM/v1/custom_types.json @@ -0,0 +1,37 @@ +[ + { + "name": "Wasabi Standalone Account", + "sourceType": "Wasabi Standalone Account", + "icon": "id-card", + "singular": "Standalone Account", + "plural": "Standalone Accounts" + }, + { + "name": "Wasabi Control Account", + "sourceType": "Wasabi Control Account", + "icon": "building-columns", + "singular": "Control Account", + "plural": "Control Accounts" + }, + { + "name": "Wasabi Channel Account", + "sourceType": "Wasabi Channel Account", + "icon": "handshake", + "singular": "Channel Account", + "plural": "Channel Accounts" + }, + { + "name": "Wasabi Sub-Account", + "sourceType": "Wasabi Sub-Account", + "icon": "building", + "singular": "Sub-Account", + "plural": "Sub-Accounts" + }, + { + "name": "Wasabi Member", + "sourceType": "Wasabi Member", + "icon": "user", + "singular": "Member", + "plural": "Members" + } +] diff --git a/plugins/WasabiWACM/v1/dataStreams/accounts.json b/plugins/WasabiWACM/v1/dataStreams/accounts.json new file mode 100644 index 00000000..1ed3fa5e --- /dev/null +++ b/plugins/WasabiWACM/v1/dataStreams/accounts.json @@ -0,0 +1,40 @@ +{ + "name": "accounts", + "displayName": "Standalone Accounts", + "description": "All Wasabi Standalone Accounts, one row per account", + "tags": ["Accounts"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/api/v1/accounts", + "pathToData": "data.items", + "paging": { + "mode": "offset", + "pageSize": { "realm": "queryArg", "path": "size", "value": "100" }, + "offset": { + "mode": "page", + "rowCountIn": { "realm": "payloadArraySize", "path": "data.items" }, + "base": 0 + }, + "out": { "realm": "queryArg", "path": "page" } + } + }, + "matches": "none", + "visibility": { "type": "hidden" }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "role": "id" }, + { + "name": "name", + "displayName": "Name", + "computed": true, + "valueExpression": "{{ $['companyName'] || (($['firstName'] || '') + ' ' + ($['lastName'] || '')).trim() }}", + "role": "label" + }, + { "name": "firstName", "visible": false }, + { "name": "lastName", "visible": false }, + { "name": "email", "visible": false }, + { "name": "phoneNumber", "visible": false }, + { "pattern": ".*" } + ], + "timeframes": false +} diff --git a/plugins/WasabiWACM/v1/dataStreams/channelAccounts.json b/plugins/WasabiWACM/v1/dataStreams/channelAccounts.json new file mode 100644 index 00000000..c19441ac --- /dev/null +++ b/plugins/WasabiWACM/v1/dataStreams/channelAccounts.json @@ -0,0 +1,32 @@ +{ + "name": "channelAccounts", + "displayName": "Channel Accounts", + "description": "All Wasabi Channel Accounts, one row per account", + "tags": ["Accounts"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/api/v1/channel-accounts", + "pathToData": "data.items", + "paging": { + "mode": "offset", + "pageSize": { "realm": "queryArg", "path": "size", "value": "100" }, + "offset": { + "mode": "page", + "rowCountIn": { "realm": "payloadArraySize", "path": "data.items" }, + "base": 0 + }, + "out": { "realm": "queryArg", "path": "page" } + } + }, + "matches": "none", + "visibility": { "type": "hidden" }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "role": "id" }, + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { "name": "contactEmail", "visible": false }, + { "name": "billingEmail", "visible": false }, + { "pattern": ".*" } + ], + "timeframes": false +} diff --git a/plugins/WasabiWACM/v1/dataStreams/controlAccountBucketUtilization.json b/plugins/WasabiWACM/v1/dataStreams/controlAccountBucketUtilization.json new file mode 100644 index 00000000..ce6805fc --- /dev/null +++ b/plugins/WasabiWACM/v1/dataStreams/controlAccountBucketUtilization.json @@ -0,0 +1,52 @@ +{ + "name": "controlAccountBucketUtilization", + "displayName": "Control Account Bucket Utilization", + "description": "Per-bucket storage utilization for a control account", + "tags": ["Accounts", "Usage"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/api/v1/control-accounts/{{object.rawId}}/buckets", + "getArgs": [ + { "key": "from", "value": "{{timeframe.start.split('T')[0]}}" }, + { "key": "to", "value": "{{timeframe.end.split('T')[0]}}" } + ], + "pathToData": "data.items", + "paging": { + "mode": "offset", + "pageSize": { "realm": "queryArg", "path": "size", "value": "100" }, + "offset": { + "mode": "page", + "rowCountIn": { "realm": "payloadArraySize", "path": "data.items" }, + "base": 0 + }, + "out": { "realm": "queryArg", "path": "page" } + } + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Wasabi Control Account"] } }, + "metadata": [ + { "name": "name", "displayName": "Bucket Name", "shape": "string", "role": "label" }, + { "name": "region", "displayName": "Region", "shape": "string" }, + { "name": "bucketNumber", "displayName": "Bucket Number", "shape": "string" }, + { "name": "startTime", "displayName": "Start Time", "shape": "date", "role": "timestamp" }, + { "name": "endTime", "displayName": "End Time", "shape": "date" }, + { "name": "activeStorage", "displayName": "Active Storage (TB)", "shape": "number" }, + { "name": "deletedStorage", "displayName": "Deleted Storage (TB)", "shape": "number" }, + { "name": "activeObjects", "displayName": "Active Objects", "shape": "number" }, + { "name": "deletedObjects", "displayName": "Deleted Objects", "shape": "number" }, + { "name": "apiCalls", "displayName": "API Calls", "shape": "number" }, + { "name": "egress", "displayName": "Egress (GB)", "shape": "number" }, + { "name": "ingress", "displayName": "Ingress (GB)", "shape": "number" }, + { "pattern": ".*" } + ], + "timeframes": [ + "last7days", + "last30days", + "thisMonth", + "lastMonth", + "thisQuarter", + "lastQuarter", + "thisYear", + "lastYear" + ] +} diff --git a/plugins/WasabiWACM/v1/dataStreams/controlAccountSummary.json b/plugins/WasabiWACM/v1/dataStreams/controlAccountSummary.json new file mode 100644 index 00000000..aba5ca7f --- /dev/null +++ b/plugins/WasabiWACM/v1/dataStreams/controlAccountSummary.json @@ -0,0 +1,43 @@ +{ + "name": "controlAccountSummary", + "displayName": "Control Account Summary", + "description": "Current state and storage of a control account", + "tags": ["Accounts"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/api/v1/control-accounts/{{object.rawId}}", + "getArgs": [{ "key": "includeApiKey", "value": "false" }], + "pathToData": "data", + "postRequestScript": "controlAccountSummary.js" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Wasabi Control Account"] } }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "role": "id", "visible": false }, + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { + "name": "status", + "displayName": "Status", + "shape": [ + "state", + { + "map": { + "success": ["Active"], + "error": ["Deactivated"] + } + } + ] + }, + { "name": "subAccountStorage", "displayName": "Sub-Account Storage", "shape": "bytes" }, + { "name": "controlAccountStorage", "displayName": "Control Account Storage", "shape": "bytes" }, + { "name": "totalStorage", "displayName": "Total Storage", "shape": "bytes" }, + { "name": "defaultPurchasedStorageTB", "displayName": "Default Purchased Storage (TB)", "shape": "number" }, + { "name": "subAccountsCount", "displayName": "Sub-Accounts Count", "shape": "number" }, + { "name": "channelAccountsCount", "displayName": "Channel Accounts Count", "shape": "number" }, + { "name": "storageQuotaType", "displayName": "Storage Quota Type", "shape": "string" }, + { "name": "primaryApiKey", "visible": false }, + { "name": "secondaryApiKey", "visible": false }, + { "pattern": ".*" } + ], + "timeframes": false +} diff --git a/plugins/WasabiWACM/v1/dataStreams/controlAccountUsageHistory.json b/plugins/WasabiWACM/v1/dataStreams/controlAccountUsageHistory.json new file mode 100644 index 00000000..03d14480 --- /dev/null +++ b/plugins/WasabiWACM/v1/dataStreams/controlAccountUsageHistory.json @@ -0,0 +1,52 @@ +{ + "name": "controlAccountUsageHistory", + "displayName": "Control Account Usage History", + "description": "Periodic storage and traffic usage records for a control account", + "tags": ["Accounts", "Usage"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/api/v1/control-accounts/usages", + "getArgs": [ + { "key": "controlAccountId", "value": "{{object.rawId}}" }, + { "key": "from", "value": "{{timeframe.start.split('T')[0]}}" }, + { "key": "to", "value": "{{timeframe.end.split('T')[0]}}" } + ], + "pathToData": "data.items", + "paging": { + "mode": "offset", + "pageSize": { "realm": "queryArg", "path": "size", "value": "100" }, + "offset": { + "mode": "page", + "rowCountIn": { "realm": "payloadArraySize", "path": "data.items" }, + "base": 0 + }, + "out": { "realm": "queryArg", "path": "page" } + } + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Wasabi Control Account"] } }, + "metadata": [ + { "name": "startTime", "displayName": "Start Time", "shape": "date", "role": "timestamp" }, + { "name": "endTime", "displayName": "End Time", "shape": "date" }, + { "name": "activeStorage", "displayName": "Active Storage (TB)", "shape": ["number", { "decimalPlaces": 4 }] }, + { "name": "deletedStorage", "displayName": "Deleted Storage (TB)", "shape": ["number", { "decimalPlaces": 4 }] }, + { "name": "storageWrote", "displayName": "Storage Written (TB)", "shape": ["number", { "decimalPlaces": 4 }] }, + { "name": "storageRead", "displayName": "Storage Read (TB)", "shape": ["number", { "decimalPlaces": 4 }] }, + { "name": "activeObjects", "displayName": "Active Objects", "shape": "number" }, + { "name": "deletedObjects", "displayName": "Deleted Objects", "shape": "number" }, + { "name": "apiCalls", "displayName": "API Calls", "shape": "number" }, + { "name": "egress", "displayName": "Egress (GB)", "shape": "number" }, + { "name": "ingress", "displayName": "Ingress (GB)", "shape": "number" }, + { "pattern": ".*" } + ], + "timeframes": [ + "last7days", + "last30days", + "thisMonth", + "lastMonth", + "thisQuarter", + "lastQuarter", + "thisYear", + "lastYear" + ] +} diff --git a/plugins/WasabiWACM/v1/dataStreams/controlAccounts.json b/plugins/WasabiWACM/v1/dataStreams/controlAccounts.json new file mode 100644 index 00000000..b9e2d4a2 --- /dev/null +++ b/plugins/WasabiWACM/v1/dataStreams/controlAccounts.json @@ -0,0 +1,36 @@ +{ + "name": "controlAccounts", + "displayName": "Control Accounts", + "description": "All Wasabi Control Accounts, one row per account", + "tags": ["Accounts"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/api/v1/control-accounts", + "getArgs": [{ "key": "includeApiKey", "value": "false" }], + "pathToData": "data.items", + "paging": { + "mode": "offset", + "pageSize": { "realm": "queryArg", "path": "size", "value": "100" }, + "offset": { + "mode": "page", + "rowCountIn": { "realm": "payloadArraySize", "path": "data.items" }, + "base": 0 + }, + "out": { "realm": "queryArg", "path": "page" } + } + }, + "matches": "none", + "visibility": { "type": "hidden" }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "role": "id" }, + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { "name": "primaryApiKey", "visible": false }, + { "name": "secondaryApiKey", "visible": false }, + { "name": "contactEmail", "visible": false }, + { "name": "billingEmail", "visible": false }, + { "name": "controlAccountEmail", "visible": false }, + { "pattern": ".*" } + ], + "timeframes": false +} diff --git a/plugins/WasabiWACM/v1/dataStreams/members.json b/plugins/WasabiWACM/v1/dataStreams/members.json new file mode 100644 index 00000000..b1cfddac --- /dev/null +++ b/plugins/WasabiWACM/v1/dataStreams/members.json @@ -0,0 +1,41 @@ +{ + "name": "members", + "displayName": "Members", + "description": "All Wasabi Sub-Account users, one row per member", + "tags": ["Accounts", "Users"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/api/v1/members", + "pathToData": "data.items", + "paging": { + "mode": "offset", + "pageSize": { "realm": "queryArg", "path": "size", "value": "100" }, + "offset": { + "mode": "page", + "rowCountIn": { "realm": "payloadArraySize", "path": "data.items" }, + "base": 0 + }, + "out": { "realm": "queryArg", "path": "page" } + } + }, + "matches": "none", + "visibility": { "type": "hidden" }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "role": "id" }, + { + "name": "name", + "displayName": "Name", + "computed": true, + "valueExpression": "{{ ((($['firstName'] || '') + ' ' + ($['lastName'] || '')).trim()) || $['username'] }}", + "role": "label" + }, + { "name": "firstName", "visible": false }, + { "name": "lastName", "visible": false }, + { "name": "username", "visible": false }, + { "name": "email", "visible": false }, + { "name": "phone", "visible": false }, + { "pattern": ".*" } + ], + "timeframes": false +} diff --git a/plugins/WasabiWACM/v1/dataStreams/scripts/controlAccountSummary.js b/plugins/WasabiWACM/v1/dataStreams/scripts/controlAccountSummary.js new file mode 100644 index 00000000..bb82f5fe --- /dev/null +++ b/plugins/WasabiWACM/v1/dataStreams/scripts/controlAccountSummary.js @@ -0,0 +1,19 @@ +// Wasabi reports subAccountStorage/controlAccountStorage/totalStorage in TB; +// convert to bytes to match the "bytes" shape used for these fields. Wasabi +// defines 1 TB as 1024 GB for storage billing/utilization, not decimal 10^12. +const TB_TO_BYTES = 1024 ** 4; + +function toBytes(value) { + return typeof value === "number" ? value * TB_TO_BYTES : value; +} + +const account = (data && data.data) || {}; + +result = [ + { + ...account, + subAccountStorage: toBytes(account.subAccountStorage), + controlAccountStorage: toBytes(account.controlAccountStorage), + totalStorage: toBytes(account.totalStorage), + }, +]; diff --git a/plugins/WasabiWACM/v1/dataStreams/scripts/subAccountInvoices.js b/plugins/WasabiWACM/v1/dataStreams/scripts/subAccountInvoices.js new file mode 100644 index 00000000..99024e7a --- /dev/null +++ b/plugins/WasabiWACM/v1/dataStreams/scripts/subAccountInvoices.js @@ -0,0 +1,17 @@ +// Wasabi reports totalStorage/activeStorage/deletedStorage in TB; convert to +// bytes to match the "bytes" shape used for these fields. Wasabi defines +// 1 TB as 1024 GB for storage billing/utilization, not the decimal 10^12. +const TB_TO_BYTES = 1024 ** 4; + +function toBytes(value) { + return typeof value === "number" ? value * TB_TO_BYTES : value; +} + +const items = (data && data.data && data.data.items) || []; + +result = items.map((item) => ({ + ...item, + totalStorage: toBytes(item.totalStorage), + activeStorage: toBytes(item.activeStorage), + deletedStorage: toBytes(item.deletedStorage), +})); diff --git a/plugins/WasabiWACM/v1/dataStreams/scripts/subAccountUsageHistory.js b/plugins/WasabiWACM/v1/dataStreams/scripts/subAccountUsageHistory.js new file mode 100644 index 00000000..21b17d75 --- /dev/null +++ b/plugins/WasabiWACM/v1/dataStreams/scripts/subAccountUsageHistory.js @@ -0,0 +1,21 @@ +// Wasabi reports storage fields in TB and traffic fields in GB; convert both +// to bytes to match the "bytes" shape used for these fields. Wasabi defines +// 1 TB as 1024 GB for storage billing/utilization, not the decimal 10^12. +const TB_TO_BYTES = 1024 ** 4; +const GB_TO_BYTES = 1e9; + +function scale(value, factor) { + return typeof value === "number" ? value * factor : value; +} + +const items = (data && data.data && data.data.items) || []; + +result = items.map((item) => ({ + ...item, + activeStorage: scale(item.activeStorage, TB_TO_BYTES), + deletedStorage: scale(item.deletedStorage, TB_TO_BYTES), + storageWrote: scale(item.storageWrote, TB_TO_BYTES), + storageRead: scale(item.storageRead, TB_TO_BYTES), + egress: scale(item.egress, GB_TO_BYTES), + ingress: scale(item.ingress, GB_TO_BYTES), +})); diff --git a/plugins/WasabiWACM/v1/dataStreams/subAccountBucketUtilization.json b/plugins/WasabiWACM/v1/dataStreams/subAccountBucketUtilization.json new file mode 100644 index 00000000..fab5b09b --- /dev/null +++ b/plugins/WasabiWACM/v1/dataStreams/subAccountBucketUtilization.json @@ -0,0 +1,52 @@ +{ + "name": "subAccountBucketUtilization", + "displayName": "Sub-Account Bucket Utilization", + "description": "Per-bucket storage utilization for a sub-account", + "tags": ["Accounts", "Usage"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/api/v1/sub-accounts/{{object.rawId}}/buckets", + "getArgs": [ + { "key": "from", "value": "{{timeframe.start.split('T')[0]}}" }, + { "key": "to", "value": "{{timeframe.end.split('T')[0]}}" } + ], + "pathToData": "data.items", + "paging": { + "mode": "offset", + "pageSize": { "realm": "queryArg", "path": "size", "value": "100" }, + "offset": { + "mode": "page", + "rowCountIn": { "realm": "payloadArraySize", "path": "data.items" }, + "base": 0 + }, + "out": { "realm": "queryArg", "path": "page" } + } + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Wasabi Sub-Account"] } }, + "metadata": [ + { "name": "name", "displayName": "Bucket Name", "shape": "string", "role": "label" }, + { "name": "region", "displayName": "Region", "shape": "string" }, + { "name": "bucketNumber", "displayName": "Bucket Number", "shape": "string" }, + { "name": "startTime", "displayName": "Start Time", "shape": "date", "role": "timestamp" }, + { "name": "endTime", "displayName": "End Time", "shape": "date" }, + { "name": "activeStorage", "displayName": "Active Storage (TB)", "shape": "number" }, + { "name": "deletedStorage", "displayName": "Deleted Storage (TB)", "shape": "number" }, + { "name": "activeObjects", "displayName": "Active Objects", "shape": "number" }, + { "name": "deletedObjects", "displayName": "Deleted Objects", "shape": "number" }, + { "name": "apiCalls", "displayName": "API Calls", "shape": "number" }, + { "name": "egress", "displayName": "Egress (GB)", "shape": "number" }, + { "name": "ingress", "displayName": "Ingress (GB)", "shape": "number" }, + { "pattern": ".*" } + ], + "timeframes": [ + "last7days", + "last30days", + "thisMonth", + "lastMonth", + "thisQuarter", + "lastQuarter", + "thisYear", + "lastYear" + ] +} diff --git a/plugins/WasabiWACM/v1/dataStreams/subAccountInvoices.json b/plugins/WasabiWACM/v1/dataStreams/subAccountInvoices.json new file mode 100644 index 00000000..ad95ecc2 --- /dev/null +++ b/plugins/WasabiWACM/v1/dataStreams/subAccountInvoices.json @@ -0,0 +1,175 @@ +{ + "name": "subAccountInvoices", + "displayName": "Sub-Account Invoices", + "description": "Billed cost line items for a sub-account", + "tags": [ + "Accounts", + "Cost" + ], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/api/v1/invoices", + "getArgs": [ + { + "key": "subAccountId", + "value": "{{object.rawId}}" + }, + { + "key": "from", + "value": "{{timeframe.start.split('T')[0]}}" + }, + { + "key": "to", + "value": "{{timeframe.end.split('T')[0]}}" + } + ], + "pathToData": "data.items", + "postRequestScript": "subAccountInvoices.js", + "paging": { + "mode": "offset", + "pageSize": { + "realm": "queryArg", + "path": "size", + "value": "100" + }, + "offset": { + "mode": "page", + "rowCountIn": { + "realm": "payloadArraySize", + "path": "data.items" + }, + "base": 0 + }, + "out": { + "realm": "queryArg", + "path": "page" + } + } + }, + "matches": { + "sourceType": { + "type": "oneOf", + "values": [ + "Wasabi Sub-Account" + ] + } + }, + "metadata": [ + { + "name": "periodStart", + "displayName": "Period Start", + "shape": "date", + "role": "timestamp" + }, + { + "name": "periodEnd", + "displayName": "Period End", + "shape": "date" + }, + { + "name": "totalStorage", + "displayName": "Total Storage", + "shape": "bytes" + }, + { + "name": "activeStorage", + "displayName": "Active Storage", + "shape": "bytes" + }, + { + "name": "deletedStorage", + "displayName": "Deleted Storage", + "shape": "bytes" + }, + { + "name": "activeStorageTotalCost", + "displayName": "Active Storage Cost", + "shape": [ + "currency", + { + "decimalPlaces": 2, + "code": "usd", + "thousandsSeparator": true + } + ] + }, + { + "name": "deletedStorageTotalCost", + "displayName": "Deleted Storage Cost", + "shape": [ + "currency", + { + "decimalPlaces": 2, + "code": "usd", + "thousandsSeparator": true + } + ] + }, + { + "name": "apiCallsTotalCost", + "displayName": "API Calls Cost", + "shape": [ + "currency", + { + "decimalPlaces": 2, + "code": "usd", + "thousandsSeparator": true + } + ] + }, + { + "name": "ingressTotalCost", + "displayName": "Ingress Cost", + "shape": [ + "currency", + { + "decimalPlaces": 2, + "code": "usd", + "thousandsSeparator": true + } + ] + }, + { + "name": "egressTotalCost", + "displayName": "Egress Cost", + "shape": [ + "currency", + { + "decimalPlaces": 2, + "code": "usd", + "thousandsSeparator": true + } + ] + }, + { + "name": "minimumActiveStorageTotalCost", + "displayName": "Minimum Active Storage Cost", + "shape": [ + "currency", + { + "decimalPlaces": 2, + "code": "usd", + "thousandsSeparator": true + } + ] + }, + { + "name": "subInvoiceId", + "displayName": "Sub-Invoice ID", + "shape": "string" + }, + { + "pattern": ".*" + } + ], + "timeframes": [ + "last30days", + "thisMonth", + "lastMonth", + "thisQuarter", + "lastQuarter", + "thisYear", + "lastYear" + ] +} \ No newline at end of file diff --git a/plugins/WasabiWACM/v1/dataStreams/subAccountSummary.json b/plugins/WasabiWACM/v1/dataStreams/subAccountSummary.json new file mode 100644 index 00000000..4b8f0726 --- /dev/null +++ b/plugins/WasabiWACM/v1/dataStreams/subAccountSummary.json @@ -0,0 +1,40 @@ +{ + "name": "subAccountSummary", + "displayName": "Sub-Account Summary", + "description": "Current state and quota of a sub-account", + "tags": ["Accounts"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/api/v1/sub-accounts/{{object.rawId}}", + "getArgs": [{ "key": "includeKeys", "value": "false" }], + "pathToData": "data" + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Wasabi Sub-Account"] } }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "role": "id", "visible": false }, + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { + "name": "status", + "displayName": "Status", + "shape": [ + "state", + { + "map": { + "success": ["PAID_ACCOUNT", "ON_TRIAL"], + "warning": ["SUSPENDED"], + "error": ["DEACTIVATED"] + } + } + ] + }, + { "name": "activeStorage", "displayName": "Active Storage (TB)", "shape": "number" }, + { "name": "deletedStorage", "displayName": "Deleted Storage (TB)", "shape": "number" }, + { "name": "purchasedStorageTB", "displayName": "Purchased Storage (TB)", "shape": "number" }, + { "name": "trialQuotaTB", "displayName": "Trial Quota (TB)", "shape": "number" }, + { "name": "accessKey", "visible": false }, + { "name": "secretKey", "visible": false }, + { "pattern": ".*" } + ], + "timeframes": false +} diff --git a/plugins/WasabiWACM/v1/dataStreams/subAccountUsageHistory.json b/plugins/WasabiWACM/v1/dataStreams/subAccountUsageHistory.json new file mode 100644 index 00000000..2e196202 --- /dev/null +++ b/plugins/WasabiWACM/v1/dataStreams/subAccountUsageHistory.json @@ -0,0 +1,53 @@ +{ + "name": "subAccountUsageHistory", + "displayName": "Sub-Account Usage History", + "description": "Periodic storage and traffic usage records for a sub-account", + "tags": ["Accounts", "Usage"], + "baseDataSourceName": "httpRequestScopedSingle", + "config": { + "httpMethod": "get", + "endpointPath": "/api/v1/usages", + "getArgs": [ + { "key": "subAccountId", "value": "{{object.rawId}}" }, + { "key": "from", "value": "{{timeframe.start.split('T')[0]}}" }, + { "key": "to", "value": "{{timeframe.end.split('T')[0]}}" } + ], + "pathToData": "data.items", + "postRequestScript": "subAccountUsageHistory.js", + "paging": { + "mode": "offset", + "pageSize": { "realm": "queryArg", "path": "size", "value": "100" }, + "offset": { + "mode": "page", + "rowCountIn": { "realm": "payloadArraySize", "path": "data.items" }, + "base": 0 + }, + "out": { "realm": "queryArg", "path": "page" } + } + }, + "matches": { "sourceType": { "type": "oneOf", "values": ["Wasabi Sub-Account"] } }, + "metadata": [ + { "name": "startTime", "displayName": "Start Time", "shape": "date", "role": "timestamp" }, + { "name": "endTime", "displayName": "End Time", "shape": "date" }, + { "name": "activeStorage", "displayName": "Active Storage", "shape": "bytes" }, + { "name": "deletedStorage", "displayName": "Deleted Storage", "shape": "bytes" }, + { "name": "storageWrote", "displayName": "Storage Written", "shape": "bytes" }, + { "name": "storageRead", "displayName": "Storage Read", "shape": "bytes" }, + { "name": "activeObjects", "displayName": "Active Objects", "shape": "number" }, + { "name": "deletedObjects", "displayName": "Deleted Objects", "shape": "number" }, + { "name": "apiCalls", "displayName": "API Calls", "shape": "number" }, + { "name": "egress", "displayName": "Egress", "shape": "bytes" }, + { "name": "ingress", "displayName": "Ingress", "shape": "bytes" }, + { "pattern": ".*" } + ], + "timeframes": [ + "last7days", + "last30days", + "thisMonth", + "lastMonth", + "thisQuarter", + "lastQuarter", + "thisYear", + "lastYear" + ] +} diff --git a/plugins/WasabiWACM/v1/dataStreams/subAccounts.json b/plugins/WasabiWACM/v1/dataStreams/subAccounts.json new file mode 100644 index 00000000..cc812802 --- /dev/null +++ b/plugins/WasabiWACM/v1/dataStreams/subAccounts.json @@ -0,0 +1,37 @@ +{ + "name": "subAccounts", + "displayName": "Sub-Accounts", + "description": "All Wasabi Sub-Accounts, one row per account", + "tags": ["Accounts"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/api/v1/sub-accounts", + "getArgs": [{ "key": "includeKeys", "value": "false" }], + "pathToData": "data.items", + "paging": { + "mode": "offset", + "pageSize": { "realm": "queryArg", "path": "size", "value": "100" }, + "offset": { + "mode": "page", + "rowCountIn": { "realm": "payloadArraySize", "path": "data.items" }, + "base": 0 + }, + "out": { "realm": "queryArg", "path": "page" } + } + }, + "matches": "none", + "visibility": { "type": "hidden" }, + "metadata": [ + { "name": "id", "displayName": "ID", "shape": "string", "role": "id" }, + { "name": "name", "displayName": "Name", "shape": "string", "role": "label" }, + { "name": "accessKey", "visible": false }, + { "name": "secretKey", "visible": false }, + { "name": "contactEmail", "visible": false }, + { "name": "billingEmail", "visible": false }, + { "name": "channelAccountEmail", "visible": false }, + { "name": "controlAccountEmail", "visible": false }, + { "pattern": ".*" } + ], + "timeframes": false +} diff --git a/plugins/WasabiWACM/v1/dataStreams/wacmConfigValidation.json b/plugins/WasabiWACM/v1/dataStreams/wacmConfigValidation.json new file mode 100644 index 00000000..9afdad9d --- /dev/null +++ b/plugins/WasabiWACM/v1/dataStreams/wacmConfigValidation.json @@ -0,0 +1,15 @@ +{ + "name": "wacmConfigValidation", + "displayName": "WACM Config Validation", + "description": "Minimal authenticated call to confirm credentials are valid", + "tags": ["Reference"], + "baseDataSourceName": "httpRequestUnscoped", + "config": { + "httpMethod": "get", + "endpointPath": "/api/v1/sub-accounts/" + }, + "matches": "none", + "visibility": { "type": "hidden" }, + "metadata": [{ "pattern": ".*" }], + "timeframes": false +} diff --git a/plugins/WasabiWACM/v1/defaultContent/channelAccountDashboard.dash.json b/plugins/WasabiWACM/v1/defaultContent/channelAccountDashboard.dash.json new file mode 100644 index 00000000..9dfabe8d --- /dev/null +++ b/plugins/WasabiWACM/v1/defaultContent/channelAccountDashboard.dash.json @@ -0,0 +1,49 @@ +{ + "name": "Channel Account", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": ["{{variables.[Channel Account]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "05c49dbb-fe02-4afa-94fc-72c40ff22864", + "x": 0, + "y": 0, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Properties", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "datastream-properties", + "name": "properties" + }, + "scope": { + "scope": "{{scopes.[Channel Accounts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Channel Account]}}" + }, + "variables": ["{{variables.[Channel Account]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true, + "hiddenColumns": ["id", "links", "type", "label", "sourceId", "sourceType"] + } + } + } + } + } + ] + } +} diff --git a/plugins/WasabiWACM/v1/defaultContent/controlAccountDashboard.dash.json b/plugins/WasabiWACM/v1/defaultContent/controlAccountDashboard.dash.json new file mode 100644 index 00000000..c40f48ee --- /dev/null +++ b/plugins/WasabiWACM/v1/defaultContent/controlAccountDashboard.dash.json @@ -0,0 +1,299 @@ +{ + "name": "Control Account", + "schemaVersion": "1.5", + "timeframe": "last30days", + "variables": ["{{variables.[Control Account]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "b9ee9587-3c24-40d5-ad5d-e3f3b66cd077", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Status", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[controlAccountSummary]}}", + "name": "controlAccountSummary", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Control Accounts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Control Account]}}" + }, + "variables": ["{{variables.[Control Account]}}"], + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "status", + "comparisonColumn": "none", + "label": "Status" + } + } + } + } + }, + { + "i": "765a8f8e-f694-4f78-a740-1a18770da6da", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Total Storage", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[controlAccountSummary]}}", + "name": "controlAccountSummary", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Control Accounts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Control Account]}}" + }, + "variables": ["{{variables.[Control Account]}}"], + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "totalStorage", + "comparisonColumn": "none", + "label": "Total Storage" + } + } + } + } + }, + { + "i": "092d2f44-0f35-43cf-8376-d9566be6c82b", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Sub-Accounts Count", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[controlAccountSummary]}}", + "name": "controlAccountSummary", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Control Accounts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Control Account]}}" + }, + "variables": ["{{variables.[Control Account]}}"], + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "subAccountsCount", + "comparisonColumn": "none", + "label": "Sub-Accounts Count" + } + } + } + } + }, + { + "i": "fc5ad0e6-37cf-422e-a082-02b077ee6b22", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Channel Accounts Count", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[controlAccountSummary]}}", + "name": "controlAccountSummary", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Control Accounts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Control Account]}}" + }, + "variables": ["{{variables.[Control Account]}}"], + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "channelAccountsCount", + "comparisonColumn": "none", + "label": "Channel Accounts Count" + } + } + } + } + }, + { + "i": "a9e851a7-366f-48fc-8f04-4aa924bfc8fb", + "x": 0, + "y": 2, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Properties", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "datastream-properties", + "name": "properties" + }, + "scope": { + "scope": "{{scopes.[Control Accounts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Control Account]}}" + }, + "variables": ["{{variables.[Control Account]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true, + "hiddenColumns": ["id", "links", "type", "label", "sourceId", "sourceType"] + } + } + } + } + }, + { + "i": "1e9b4d24-142a-4e76-ac14-59b1c6ace958", + "x": 1, + "y": 2, + "w": 3, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Usage History", + "description": "Daily storage usage over the dashboard timeframe", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[controlAccountUsageHistory]}}", + "name": "controlAccountUsageHistory", + "pluginConfigId": "{{configId}}", + "group": { + "by": [["startTime", "byDay"]], + "aggregate": [ + { "type": "sum", "names": ["activeStorage"] }, + { "type": "sum", "names": ["deletedStorage"] } + ] + }, + "sort": { "by": [["startTime_byDay", "asc"]] } + }, + "scope": { + "scope": "{{scopes.[Control Accounts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Control Account]}}" + }, + "variables": ["{{variables.[Control Account]}}"], + "visualisation": { + "type": "data-stream-line-graph", + "config": { + "data-stream-line-graph": { + "xAxisColumn": "startTime_byDay", + "yAxisColumn": ["activeStorage_sum", "deletedStorage_sum"], + "seriesColumn": "none", + "showLegend": true, + "legendPosition": "bottom", + "yAxisLabel": "Storage (TB)", + "showYAxisLabel": true, + "showTrendLine": false + } + } + } + } + }, + { + "i": "8e648227-c704-4c97-b58d-8ef53fe52a01", + "x": 0, + "y": 5, + "w": 4, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Bucket Utilization", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[controlAccountBucketUtilization]}}", + "name": "controlAccountBucketUtilization", + "pluginConfigId": "{{configId}}", + "sort": { "by": [["activeStorage", "desc"]] } + }, + "scope": { + "scope": "{{scopes.[Control Accounts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Control Account]}}" + }, + "variables": ["{{variables.[Control Account]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "name", + "region", + "activeStorage", + "deletedStorage", + "activeObjects", + "deletedObjects", + "apiCalls", + "egress", + "ingress" + ], + "hiddenColumns": ["bucketNumber", "startTime", "endTime"] + } + } + } + } + } + ] + } +} diff --git a/plugins/WasabiWACM/v1/defaultContent/manifest.json b/plugins/WasabiWACM/v1/defaultContent/manifest.json new file mode 100644 index 00000000..6c9986f4 --- /dev/null +++ b/plugins/WasabiWACM/v1/defaultContent/manifest.json @@ -0,0 +1,9 @@ +{ + "items": [ + { "name": "overview", "type": "dashboard" }, + { "name": "controlAccountDashboard", "type": "dashboard" }, + { "name": "channelAccountDashboard", "type": "dashboard" }, + { "name": "subAccountDashboard", "type": "dashboard" }, + { "name": "standaloneAccountDashboard", "type": "dashboard" } + ] +} diff --git a/plugins/WasabiWACM/v1/defaultContent/overview.dash.json b/plugins/WasabiWACM/v1/defaultContent/overview.dash.json new file mode 100644 index 00000000..666ac56e --- /dev/null +++ b/plugins/WasabiWACM/v1/defaultContent/overview.dash.json @@ -0,0 +1,291 @@ +{ + "name": "Overview", + "schemaVersion": "1.5", + "timeframe": "none", + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "7ae96803-bf20-4b4e-a55b-8b601f364657", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Total Sub-Accounts", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "group": { "by": [], "aggregate": [{ "type": "count" }] } + }, + "scope": { + "scope": "{{scopes.[Sub-Accounts]}}", + "workspace": "{{workspaceId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Total Sub-Accounts" + } + } + } + } + }, + { + "i": "e7bbb607-ca23-4d04-8dd7-122ba6841c51", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Total Active Storage", + "description": "Sum of active storage across all sub-accounts", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "group": { "by": [], "aggregate": [{ "type": "sum", "names": ["activeStorage"] }] } + }, + "scope": { + "scope": "{{scopes.[Sub-Accounts]}}", + "workspace": "{{workspaceId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "activeStorage_sum", + "comparisonColumn": "none", + "label": "Total Active Storage" + } + } + } + } + }, + { + "i": "0e5bb039-c7bc-4dbd-a10e-4fed0213d2db", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Total Control Accounts", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "group": { "by": [], "aggregate": [{ "type": "count" }] } + }, + "scope": { + "scope": "{{scopes.[Control Accounts]}}", + "workspace": "{{workspaceId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Total Control Accounts" + } + } + } + } + }, + { + "i": "841b58b8-c4e5-4dce-8f39-2dde1f854e01", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Total Channel Accounts", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "group": { "by": [], "aggregate": [{ "type": "count" }] } + }, + "scope": { + "scope": "{{scopes.[Channel Accounts]}}", + "workspace": "{{workspaceId}}" + }, + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "count", + "comparisonColumn": "none", + "label": "Total Channel Accounts" + } + } + } + } + }, + { + "i": "4b6099cd-5684-459e-800f-ec18b11145ba", + "x": 0, + "y": 2, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Sub-Accounts by Status", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "group": { + "by": [["status", "uniqueValues"]], + "aggregate": [{ "type": "count" }] + } + }, + "scope": { + "scope": "{{scopes.[Sub-Accounts]}}", + "workspace": "{{workspaceId}}" + }, + "visualisation": { + "type": "data-stream-donut-chart", + "config": { + "data-stream-donut-chart": { + "valueColumn": "count", + "labelColumn": "status_uniqueValues", + "hideCenterValue": false, + "showValuesAsPercentage": false, + "legendPosition": "auto", + "legendMode": "table" + } + } + } + } + }, + { + "i": "8bb333eb-ef71-47a3-bb4c-4dba3c16976e", + "x": 1, + "y": 2, + "w": 3, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Top 10 Sub-Accounts by Active Storage", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "sort": { "by": [["activeStorage", "desc"]], "top": 10 } + }, + "scope": { + "scope": "{{scopes.[Sub-Accounts]}}", + "workspace": "{{workspaceId}}" + }, + "visualisation": { + "type": "data-stream-bar-chart", + "config": { + "data-stream-bar-chart": { + "xAxisData": "name", + "yAxisData": ["activeStorage"], + "xAxisGroup": "none", + "xAxisLabel": "", + "yAxisLabel": "Active Storage", + "showXAxisLabel": true, + "showYAxisLabel": true, + "showLegend": false, + "legendPosition": "bottom", + "showGrid": true, + "horizontalLayout": "vertical", + "displayMode": "actual", + "showTotals": false, + "showValue": false, + "grouping": false, + "range": { "type": "auto" } + } + } + } + } + }, + { + "i": "d009ed2f-31b2-4965-b9d1-16c43574770e", + "x": 0, + "y": 5, + "w": 4, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "All Sub-Accounts", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "datastream-properties", + "name": "properties", + "sort": { "by": [["activeStorage", "desc"]] } + }, + "scope": { + "scope": "{{scopes.[Sub-Accounts]}}", + "workspace": "{{workspaceId}}" + }, + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "name", + "status", + "activeStorage", + "deletedStorage", + "purchasedStorageTB", + "controlAccountName", + "channelAccountName" + ], + "hiddenColumns": ["id", "links", "type", "label", "sourceId", "sourceType"] + } + } + } + } + } + ] + } +} diff --git a/plugins/WasabiWACM/v1/defaultContent/scopes.json b/plugins/WasabiWACM/v1/defaultContent/scopes.json new file mode 100644 index 00000000..18bd4f30 --- /dev/null +++ b/plugins/WasabiWACM/v1/defaultContent/scopes.json @@ -0,0 +1,50 @@ +[ + { + "name": "Sub-Accounts", + "matches": { + "sourceType": { "type": "oneOf", "values": ["Wasabi Sub-Account"] } + }, + "variable": { + "name": "Sub-Account", + "type": "object", + "default": "none", + "allowMultipleSelection": false + } + }, + { + "name": "Control Accounts", + "matches": { + "sourceType": { "type": "oneOf", "values": ["Wasabi Control Account"] } + }, + "variable": { + "name": "Control Account", + "type": "object", + "default": "none", + "allowMultipleSelection": false + } + }, + { + "name": "Channel Accounts", + "matches": { + "sourceType": { "type": "oneOf", "values": ["Wasabi Channel Account"] } + }, + "variable": { + "name": "Channel Account", + "type": "object", + "default": "none", + "allowMultipleSelection": false + } + }, + { + "name": "Standalone Accounts", + "matches": { + "sourceType": { "type": "oneOf", "values": ["Wasabi Standalone Account"] } + }, + "variable": { + "name": "Standalone Account", + "type": "object", + "default": "none", + "allowMultipleSelection": false + } + } +] diff --git a/plugins/WasabiWACM/v1/defaultContent/standaloneAccountDashboard.dash.json b/plugins/WasabiWACM/v1/defaultContent/standaloneAccountDashboard.dash.json new file mode 100644 index 00000000..d6a38867 --- /dev/null +++ b/plugins/WasabiWACM/v1/defaultContent/standaloneAccountDashboard.dash.json @@ -0,0 +1,49 @@ +{ + "name": "Standalone Account", + "schemaVersion": "1.5", + "timeframe": "none", + "variables": ["{{variables.[Standalone Account]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "69bb9842-b955-45c4-aa6a-1f4bdad2e2ad", + "x": 0, + "y": 0, + "w": 4, + "h": 4, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Properties", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "datastream-properties", + "name": "properties" + }, + "scope": { + "scope": "{{scopes.[Standalone Accounts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Standalone Account]}}" + }, + "variables": ["{{variables.[Standalone Account]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true, + "hiddenColumns": ["id", "links", "type", "label", "sourceId", "sourceType"] + } + } + } + } + } + ] + } +} diff --git a/plugins/WasabiWACM/v1/defaultContent/subAccountDashboard.dash.json b/plugins/WasabiWACM/v1/defaultContent/subAccountDashboard.dash.json new file mode 100644 index 00000000..d8324603 --- /dev/null +++ b/plugins/WasabiWACM/v1/defaultContent/subAccountDashboard.dash.json @@ -0,0 +1,344 @@ +{ + "name": "Sub-Account", + "schemaVersion": "1.5", + "timeframe": "last30days", + "variables": ["{{variables.[Sub-Account]}}"], + "dashboard": { + "_type": "layout/grid", + "columns": 4, + "version": 1, + "contents": [ + { + "i": "6077a6b9-92ca-4b3a-b3e4-245ddc593596", + "x": 0, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Status", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[subAccountSummary]}}", + "name": "subAccountSummary", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Sub-Accounts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Sub-Account]}}" + }, + "variables": ["{{variables.[Sub-Account]}}"], + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "status", + "comparisonColumn": "none", + "label": "Status" + } + } + } + } + }, + { + "i": "c296d94b-458c-47dd-b85d-d6ff6bb4022e", + "x": 1, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Active Storage", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[subAccountSummary]}}", + "name": "subAccountSummary", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Sub-Accounts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Sub-Account]}}" + }, + "variables": ["{{variables.[Sub-Account]}}"], + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "activeStorage", + "comparisonColumn": "none", + "label": "Active Storage" + } + } + } + } + }, + { + "i": "6a8461bb-722d-4fb6-828b-81d130ad95f5", + "x": 2, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Purchased Storage (TB)", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[subAccountSummary]}}", + "name": "subAccountSummary", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Sub-Accounts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Sub-Account]}}" + }, + "variables": ["{{variables.[Sub-Account]}}"], + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "purchasedStorageTB", + "comparisonColumn": "none", + "label": "Purchased Storage (TB)" + } + } + } + } + }, + { + "i": "df489526-1623-490a-a5a5-f9d27471a1b3", + "x": 3, + "y": 0, + "w": 1, + "h": 2, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "MFA Enabled", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[subAccountSummary]}}", + "name": "subAccountSummary", + "pluginConfigId": "{{configId}}" + }, + "scope": { + "scope": "{{scopes.[Sub-Accounts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Sub-Account]}}" + }, + "variables": ["{{variables.[Sub-Account]}}"], + "visualisation": { + "type": "data-stream-scalar", + "config": { + "data-stream-scalar": { + "value": "mfaEnabled", + "comparisonColumn": "none", + "label": "MFA Enabled" + } + } + } + } + }, + { + "i": "b93c6b90-4a2d-4c01-97c6-06cb509bf8c0", + "x": 0, + "y": 2, + "w": 1, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Properties", + "description": "", + "timeframe": "none", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "datastream-properties", + "name": "properties" + }, + "scope": { + "scope": "{{scopes.[Sub-Accounts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Sub-Account]}}" + }, + "variables": ["{{variables.[Sub-Account]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": true, + "hiddenColumns": ["id", "links", "type", "label", "sourceId", "sourceType"] + } + } + } + } + }, + { + "i": "4cd92bb1-d28b-4730-aded-3472dc7bc2a1", + "x": 1, + "y": 2, + "w": 3, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Usage History", + "description": "Daily storage usage over the dashboard timeframe", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[subAccountUsageHistory]}}", + "name": "subAccountUsageHistory", + "pluginConfigId": "{{configId}}", + "group": { + "by": [["startTime", "byDay"]], + "aggregate": [ + { "type": "sum", "names": ["activeStorage"] }, + { "type": "sum", "names": ["deletedStorage"] } + ] + }, + "sort": { "by": [["startTime_byDay", "asc"]] } + }, + "scope": { + "scope": "{{scopes.[Sub-Accounts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Sub-Account]}}" + }, + "variables": ["{{variables.[Sub-Account]}}"], + "visualisation": { + "type": "data-stream-line-graph", + "config": { + "data-stream-line-graph": { + "xAxisColumn": "startTime_byDay", + "yAxisColumn": ["activeStorage_sum", "deletedStorage_sum"], + "seriesColumn": "none", + "showLegend": true, + "legendPosition": "bottom", + "yAxisLabel": "Storage (TB)", + "showYAxisLabel": true, + "showTrendLine": false + } + } + } + } + }, + { + "i": "dc8eeb8c-0538-409e-b7ab-5a2f96ff5b26", + "x": 0, + "y": 5, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Bucket Utilization", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[subAccountBucketUtilization]}}", + "name": "subAccountBucketUtilization", + "pluginConfigId": "{{configId}}", + "sort": { "by": [["activeStorage", "desc"]] } + }, + "scope": { + "scope": "{{scopes.[Sub-Accounts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Sub-Account]}}" + }, + "variables": ["{{variables.[Sub-Account]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "name", + "region", + "activeStorage", + "deletedStorage", + "activeObjects", + "apiCalls", + "egress", + "ingress" + ], + "hiddenColumns": ["bucketNumber", "startTime", "endTime", "deletedObjects"] + } + } + } + } + }, + { + "i": "75641d05-970b-4fee-b43b-3b347a74cf82", + "x": 2, + "y": 5, + "w": 2, + "h": 3, + "moved": false, + "static": false, + "z": 0, + "config": { + "_type": "tile/data-stream", + "title": "Invoices", + "description": "", + "activePluginConfigIds": ["{{configId}}"], + "dataStream": { + "id": "{{dataStreams.[subAccountInvoices]}}", + "name": "subAccountInvoices", + "pluginConfigId": "{{configId}}", + "sort": { "by": [["periodStart", "desc"]] } + }, + "scope": { + "scope": "{{scopes.[Sub-Accounts]}}", + "workspace": "{{workspaceId}}", + "variable": "{{variables.[Sub-Account]}}" + }, + "variables": ["{{variables.[Sub-Account]}}"], + "visualisation": { + "type": "data-stream-table", + "config": { + "data-stream-table": { + "transpose": false, + "columnOrder": [ + "periodStart", + "periodEnd", + "activeStorageTotalCost", + "deletedStorageTotalCost", + "apiCallsTotalCost", + "ingressTotalCost", + "egressTotalCost" + ], + "hiddenColumns": ["subInvoiceId", "minimumActiveStorageTotalCost", "totalStorage", "activeStorage", "deletedStorage"] + } + } + } + } + } + ] + } +} diff --git a/plugins/WasabiWACM/v1/docs/README.md b/plugins/WasabiWACM/v1/docs/README.md new file mode 100644 index 00000000..17433ce6 --- /dev/null +++ b/plugins/WasabiWACM/v1/docs/README.md @@ -0,0 +1,61 @@ +Monitor your [Wasabi](https://wasabi.com) reseller and multi-tenant account hierarchy in SquaredUp — Control Accounts, Channel Accounts, Sub-Accounts, storage usage, bucket utilization, and billing — via the [WACM Connect API](https://docs.wasabi.com/apidocs/wacm-connect-api). + +> ⚠️ This plugin covers the **WACM (Wasabi Account Control Manager)** account-management API only, used by Wasabi partners/resellers to manage downstream accounts. It does not cover Wasabi's S3-compatible storage API (buckets/objects data plane) — only account-level storage and billing metrics reported by WACM. + +## Setup + +You will need a WACM **API key**, paired with the corresponding account name or username as its "username". + +1. Sign in to your [Wasabi Account Control Manager console](https://console.wacm.wasabisys.com). +2. To use an **Account API Key** (recommended for Control/Channel/Sub-Account-wide access): go to the **Account** tab and generate an API key as the Account Admin. Note the **account name** shown alongside it. +3. Alternatively, to use a **User API Key** (scoped to what that user can see): open your own profile and generate a **User API key**. Note your **WACM username**. +4. Copy the key — it is shown only once — and paste it, along with the matching account name or username, into the fields below. + +> ⚠️ Regenerating an API key permanently invalidates the old one — update this plugin's configuration if you rotate keys. + +## Configuration fields + +| Field | What it is | Where to find it | Required | +| ----- | ---------- | ----------------- | -------- | +| **Username** | The WACM account name (for an Account API Key) or WACM username (for a User API Key). | WACM console → **Account** tab, or your user profile. | Yes | +| **API Key** | Authenticates every request via HTTP Basic Auth. | WACM console → **Account** tab (Account API Key) or your user profile (User API key). | Yes | + +On save, the plugin validates the credentials by calling a lightweight reference endpoint; invalid credentials fail setup with an authentication error. + +## What this plugin monitors + +- **Account hierarchy** — Standalone Accounts, Control Accounts, Channel Accounts, Sub-Accounts, and Members, with each object's key properties (status, storage quota). +- **Storage usage** — daily active/deleted storage, object counts, and traffic (egress/ingress/API calls) history per Sub-Account and Control Account. +- **Bucket utilization** — daily per-bucket storage breakdowns per Sub-Account and Control Account. +- **Billing** — monthly invoice line items and costs per Sub-Account. + +The out-of-the-box dashboards include an estate-wide **Overview** plus a perspective for each of **Control Account**, **Channel Account**, **Sub-Account**, and **Standalone Account**. + +## Data streams + +- **Sub-Account Summary** — current status and storage quota, one row per sub-account. +- **Sub-Account Usage History** — daily storage and traffic usage history for a sub-account. +- **Sub-Account Bucket Utilization** — daily per-bucket storage utilization for a sub-account. +- **Sub-Account Invoices** — monthly billed cost line items for a sub-account. +- **Control Account Summary** — current status and storage totals, one row per control account. +- **Control Account Usage History** — daily storage and traffic usage history for a control account. +- **Control Account Bucket Utilization** — daily per-bucket storage utilization for a control account. + +## What gets indexed + +| Object type | API source | Represents | +| ----------- | ---------- | ---------- | +| **Wasabi Standalone Account** | `GET /api/v1/accounts` | An independent Wasabi console account outside the reseller hierarchy. | +| **Wasabi Control Account** | `GET /api/v1/control-accounts` | The top-level billing-owning account in a reseller hierarchy. | +| **Wasabi Channel Account** | `GET /api/v1/channel-accounts` | An optional reseller/partner access layer under a Control Account. | +| **Wasabi Sub-Account** | `GET /api/v1/sub-accounts` | An actual Wasabi Console account — the entity that owns buckets and is billed. | +| **Wasabi Member** | `GET /api/v1/members` | A user with access to a Sub-Account. | + +## Known limitations + +- **Read-only** — this plugin never creates, modifies, or deletes anything in Wasabi WACM. +- **Permission tiers vary by API key.** The WACM API scopes each endpoint to the calling account's tier. A **Sub-Account**-tier key (the most common case) can see its own Sub-Accounts, Channel Accounts, and Members, but **Standalone Accounts and Control Accounts will show no indexed objects** — and their dashboards will stay empty — until a Control-Account-tier or Account-Admin key is used. +- **Usage and bucket utilization data is daily-granularity** — timeframes shorter than 7 days aren't offered. +- **Invoices are monthly billing-period records** — timeframes shorter than 30 days aren't offered, and a newly created sub-account may show no invoices until its first billing cycle completes. +- **Members aren't filtered to a specific Sub-Account** in dashboards — indexed Members can be browsed as their own object type, but no per-sub-account member table is shown on the Sub-Account perspective. +- **Channel Account and Standalone Account perspectives show only indexed properties** — the WACM API doesn't expose dedicated usage or billing endpoints for these tiers. diff --git a/plugins/WasabiWACM/v1/icon.svg b/plugins/WasabiWACM/v1/icon.svg new file mode 100644 index 00000000..a1ed4da7 --- /dev/null +++ b/plugins/WasabiWACM/v1/icon.svg @@ -0,0 +1,7 @@ + +Wasabi + + + + + diff --git a/plugins/WasabiWACM/v1/indexDefinitions/default.json b/plugins/WasabiWACM/v1/indexDefinitions/default.json new file mode 100644 index 00000000..b6e8349b --- /dev/null +++ b/plugins/WasabiWACM/v1/indexDefinitions/default.json @@ -0,0 +1,142 @@ +{ + "steps": [ + { + "name": "accounts", + "dataStream": { "name": "accounts" }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { "value": "Wasabi Standalone Account" }, + "properties": [ + "companyName", + "country", + "partnerName", + "wasabiAccountNumber", + "status", + "storageAmount" + ] + }, + "optional": true + }, + { + "name": "controlAccounts", + "dataStream": { "name": "controlAccounts" }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { "value": "Wasabi Control Account" }, + "properties": [ + "partnerType", + "accountType", + "status", + "creationDate", + "country", + "city", + "state", + "governanceAccountId", + "governanceAccountName", + "channelAccountsCount", + "subAccountsCount", + "subAccountStorage", + "controlAccountStorage", + "totalStorage", + "defaultPurchasedStorageTB", + "storageQuotaType", + "ssoEnabled" + ] + }, + "optional": true + }, + { + "name": "channelAccounts", + "dataStream": { "name": "channelAccounts" }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { "value": "Wasabi Channel Account" }, + "properties": [ + "controlAccountId", + "controlAccountName", + "governanceAccountId", + "governanceAccountName", + "purchasedStorage", + "subAccountDefaultPurchasedStorage", + "subAccountDefaultStorageQuotaType", + "storageQuotaType", + "subAccountStorage", + "billableActiveStorage", + "billableDeletedStorage", + "subAccountsCount", + "status", + "creationDate", + "country", + "city", + "state", + "deleted" + ] + }, + "optional": true + }, + { + "name": "subAccounts", + "dataStream": { "name": "subAccounts" }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { "value": "Wasabi Sub-Account" }, + "properties": [ + "partnerType", + "accountType", + "status", + "creationDate", + "country", + "city", + "state", + "channelAccountId", + "channelAccountName", + "controlAccountId", + "controlAccountName", + "governanceAccountId", + "governanceAccountName", + "wasabiAccountNumber", + "wasabiAccountName", + "ftpEnabled", + "activeStorage", + "deletedStorage", + "trialQuotaTB", + "trialExpiration", + "purchasedStorageTB", + "mfaEnabled", + "storageQuotaType", + "ssoEnabled" + ] + }, + "optional": true + }, + { + "name": "members", + "dataStream": { "name": "members" }, + "timeframe": "none", + "objectMapping": { + "id": "id", + "name": "name", + "type": { "value": "Wasabi Member" }, + "properties": [ + "subAccountId", + "memberRole", + "status", + "mfa", + "creationDate", + "country", + "city", + "isSsoUser" + ] + }, + "optional": true + } + ] +} diff --git a/plugins/WasabiWACM/v1/metadata.json b/plugins/WasabiWACM/v1/metadata.json new file mode 100644 index 00000000..1b9b555a --- /dev/null +++ b/plugins/WasabiWACM/v1/metadata.json @@ -0,0 +1,57 @@ +{ + "name": "wasabi-wacm", + "displayName": "Wasabi WACM", + "version": "1.0.3", + "author": { + "name": "@arobavet", + "type": "community" + }, + "description": "Monitor Wasabi Account Control Manager (WACM) accounts, storage usage, bucket utilization and billing across your reseller account hierarchy.", + "category": "Cloud Platforms", + "type": "hybrid", + "schemaVersion": "2.1", + "importNotSupported": false, + "restrictedToPlatforms": [], + "keywords": [ + "wasabi", + "wacm", + "cloud storage", + "s3", + "reseller", + "billing", + "storage", + "account management" + ], + "objectTypes": [ + "Wasabi Standalone Account", + "Wasabi Control Account", + "Wasabi Channel Account", + "Wasabi Sub-Account", + "Wasabi Member" + ], + "screenshots": ["https://github.com/squaredup/plugins/blob/main/plugins/WasabiWACM/v1/screenshots/wacm-subaccount-view.png"], + "links": [ + { + "category": "documentation", + "url": "https://github.com/squaredup/plugins/blob/main/plugins/WasabiWACM/v1/docs/README.md", + "label": "Help adding this plugin" + }, + { + "category": "source", + "url": "https://github.com/squaredup/plugins/tree/main/plugins/WasabiWACM/v1", + "label": "Repository" + } + ], + "base": { + "plugin": "WebAPI", + "majorVersion": "1", + "config": { + "baseUrl": "https://api.wacm.wasabisys.com", + "authMode": "basic", + "basicAuthUsername": "{{username}}", + "basicAuthPassword": "{{apiKey}}", + "headers": [], + "queryArgs": [] + } + } +} diff --git a/plugins/WasabiWACM/v1/screenshots/wacm-subaccount-view.png b/plugins/WasabiWACM/v1/screenshots/wacm-subaccount-view.png new file mode 100644 index 00000000..01f79adc Binary files /dev/null and b/plugins/WasabiWACM/v1/screenshots/wacm-subaccount-view.png differ diff --git a/plugins/WasabiWACM/v1/ui.json b/plugins/WasabiWACM/v1/ui.json new file mode 100644 index 00000000..59ad915f --- /dev/null +++ b/plugins/WasabiWACM/v1/ui.json @@ -0,0 +1,21 @@ +[ + { + "type": "text", + "name": "username", + "label": "Username", + "help": "Your WACM account name (for an Account API Key) or WACM username (for a User API key).", + "placeholder": "e.g. acme-msp or jane.doe", + "validation": { + "required": true + } + }, + { + "type": "password", + "name": "apiKey", + "label": "API Key", + "help": "The Account API key or User API key generated in the WACM console — shown only once when created.", + "validation": { + "required": true + } + } +]