@dx-do/client 6.4.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.js +475 -34
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +475 -35
- package/dist/index.esm.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/lib/model/datastore/views/index.d.ts +1 -0
- package/dist/src/lib/model/datastore/views/index.d.ts.map +1 -1
- package/dist/src/lib/model/datastore/views/service-universe.d.ts +294 -0
- package/dist/src/lib/model/datastore/views/service-universe.d.ts.map +1 -0
- package/dist/src/lib/services/authorization.service.d.ts +10 -0
- package/dist/src/lib/services/authorization.service.d.ts.map +1 -1
- package/dist/src/lib/services/datastore/datastore-service-universe.service.d.ts +129 -0
- package/dist/src/lib/services/datastore/datastore-service-universe.service.d.ts.map +1 -0
- package/dist/src/lib/services/service-monolith.d.ts +2 -0
- package/dist/src/lib/services/service-monolith.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.js
CHANGED
|
@@ -64831,6 +64831,23 @@ class AuthorizationService {
|
|
|
64831
64831
|
}
|
|
64832
64832
|
return raw;
|
|
64833
64833
|
}
|
|
64834
|
+
/**
|
|
64835
|
+
* Returns true if a user with the given identifier exists on the tenant.
|
|
64836
|
+
* Matches case-insensitively against each account's `userId` (the login
|
|
64837
|
+
* principal — usually the upper-cased email) and any of its `emailIds`.
|
|
64838
|
+
*
|
|
64839
|
+
* @remarks Fetches the user list via {@link getUsersV2} (a single page, up
|
|
64840
|
+
* to 5000 accounts). A tenant with more users than that could yield a false
|
|
64841
|
+
* negative for an account beyond the page.
|
|
64842
|
+
*/
|
|
64843
|
+
async userExists(userIdentifier) {
|
|
64844
|
+
const needle = userIdentifier.trim().toUpperCase();
|
|
64845
|
+
if (needle.length === 0)
|
|
64846
|
+
return false;
|
|
64847
|
+
const response = await this.getUsersV2(1, 5000);
|
|
64848
|
+
return response.users.some((user) => (user.userId ?? '').toUpperCase() === needle ||
|
|
64849
|
+
(user.emailIds ?? []).some((email) => (email.emailAddr ?? '').toUpperCase() === needle));
|
|
64850
|
+
}
|
|
64834
64851
|
/** Reformats a cohort id to upper-cased, dashed GUID form (`8-4-4-4-12`). */
|
|
64835
64852
|
toGuidCohortId(cohortId) {
|
|
64836
64853
|
const hex = cohortId.replace(/-/g, '').toLowerCase();
|
|
@@ -67460,46 +67477,55 @@ const coerce$1 = (version, options) => {
|
|
|
67460
67477
|
};
|
|
67461
67478
|
var coerce_1 = coerce$1;
|
|
67462
67479
|
|
|
67463
|
-
|
|
67464
|
-
|
|
67465
|
-
this.max = 1000;
|
|
67466
|
-
this.map = new Map();
|
|
67467
|
-
}
|
|
67480
|
+
var lrucache;
|
|
67481
|
+
var hasRequiredLrucache;
|
|
67468
67482
|
|
|
67469
|
-
|
|
67470
|
-
|
|
67471
|
-
|
|
67472
|
-
return undefined
|
|
67473
|
-
} else {
|
|
67474
|
-
// Remove the key from the map and add it to the end
|
|
67475
|
-
this.map.delete(key);
|
|
67476
|
-
this.map.set(key, value);
|
|
67477
|
-
return value
|
|
67478
|
-
}
|
|
67479
|
-
}
|
|
67483
|
+
function requireLrucache () {
|
|
67484
|
+
if (hasRequiredLrucache) return lrucache;
|
|
67485
|
+
hasRequiredLrucache = 1;
|
|
67480
67486
|
|
|
67481
|
-
|
|
67482
|
-
|
|
67483
|
-
|
|
67487
|
+
class LRUCache {
|
|
67488
|
+
constructor () {
|
|
67489
|
+
this.max = 1000;
|
|
67490
|
+
this.map = new Map();
|
|
67491
|
+
}
|
|
67484
67492
|
|
|
67485
|
-
|
|
67486
|
-
|
|
67493
|
+
get (key) {
|
|
67494
|
+
const value = this.map.get(key);
|
|
67495
|
+
if (value === undefined) {
|
|
67496
|
+
return undefined
|
|
67497
|
+
} else {
|
|
67498
|
+
// Remove the key from the map and add it to the end
|
|
67499
|
+
this.map.delete(key);
|
|
67500
|
+
this.map.set(key, value);
|
|
67501
|
+
return value
|
|
67502
|
+
}
|
|
67503
|
+
}
|
|
67487
67504
|
|
|
67488
|
-
|
|
67489
|
-
|
|
67490
|
-
|
|
67491
|
-
const firstKey = this.map.keys().next().value;
|
|
67492
|
-
this.delete(firstKey);
|
|
67493
|
-
}
|
|
67505
|
+
delete (key) {
|
|
67506
|
+
return this.map.delete(key)
|
|
67507
|
+
}
|
|
67494
67508
|
|
|
67495
|
-
|
|
67496
|
-
|
|
67509
|
+
set (key, value) {
|
|
67510
|
+
const deleted = this.delete(key);
|
|
67497
67511
|
|
|
67498
|
-
|
|
67499
|
-
|
|
67500
|
-
|
|
67512
|
+
if (!deleted && value !== undefined) {
|
|
67513
|
+
// If cache is full, delete the least recently used item
|
|
67514
|
+
if (this.map.size >= this.max) {
|
|
67515
|
+
const firstKey = this.map.keys().next().value;
|
|
67516
|
+
this.delete(firstKey);
|
|
67517
|
+
}
|
|
67518
|
+
|
|
67519
|
+
this.map.set(key, value);
|
|
67520
|
+
}
|
|
67501
67521
|
|
|
67502
|
-
|
|
67522
|
+
return this
|
|
67523
|
+
}
|
|
67524
|
+
}
|
|
67525
|
+
|
|
67526
|
+
lrucache = LRUCache;
|
|
67527
|
+
return lrucache;
|
|
67528
|
+
}
|
|
67503
67529
|
|
|
67504
67530
|
var range;
|
|
67505
67531
|
var hasRequiredRange;
|
|
@@ -67722,7 +67748,7 @@ function requireRange () {
|
|
|
67722
67748
|
|
|
67723
67749
|
range = Range;
|
|
67724
67750
|
|
|
67725
|
-
const LRU =
|
|
67751
|
+
const LRU = requireLrucache();
|
|
67726
67752
|
const cache = new LRU();
|
|
67727
67753
|
|
|
67728
67754
|
const parseOptions = parseOptions_1;
|
|
@@ -78831,6 +78857,419 @@ class DataStoreNASSService {
|
|
|
78831
78857
|
}
|
|
78832
78858
|
}
|
|
78833
78859
|
|
|
78860
|
+
/**
|
|
78861
|
+
* Service Universe models — an **OI-owned universe (view) scoped to a curated
|
|
78862
|
+
* list of services**, managed through the tenant `views/*` API. The wire shape
|
|
78863
|
+
* here matches the real server payload (verified against a live tenant): `views`
|
|
78864
|
+
* is an **array** of typed sub-views and access grants carry a `permissions`
|
|
78865
|
+
* array. (This is deliberately distinct from the object-shaped `ViewBodySchema`
|
|
78866
|
+
* in `./query`, which models a different, unused surface.)
|
|
78867
|
+
*/
|
|
78868
|
+
/** Access permission granted on a service universe. */
|
|
78869
|
+
const ServiceUniversePermissionSchema = v4.z
|
|
78870
|
+
.enum(['READ', 'WRITE'])
|
|
78871
|
+
.describe('Permission granted on the universe');
|
|
78872
|
+
/** A single user or group access grant on a service universe. */
|
|
78873
|
+
const ServiceUniverseAccessEntrySchema = v4.z.looseObject({
|
|
78874
|
+
name: v4.z.string().describe('User email or group name being granted access'),
|
|
78875
|
+
permissions: v4.z
|
|
78876
|
+
.array(ServiceUniversePermissionSchema)
|
|
78877
|
+
.optional()
|
|
78878
|
+
.describe('Permissions granted to this principal (READ and/or WRITE)'),
|
|
78879
|
+
canManage: v4.z
|
|
78880
|
+
.boolean()
|
|
78881
|
+
.optional()
|
|
78882
|
+
.describe('Whether the principal may manage (re-share) the universe'),
|
|
78883
|
+
attributeInclude: v4.z
|
|
78884
|
+
.array(v4.z.unknown())
|
|
78885
|
+
.optional()
|
|
78886
|
+
.describe('Attribute-based include scoping for this grant'),
|
|
78887
|
+
attributeExclude: v4.z
|
|
78888
|
+
.array(v4.z.unknown())
|
|
78889
|
+
.optional()
|
|
78890
|
+
.describe('Attribute-based exclude scoping for this grant'),
|
|
78891
|
+
isValid: v4.z
|
|
78892
|
+
.array(v4.z.string())
|
|
78893
|
+
.optional()
|
|
78894
|
+
.describe('Server-side validation hints for the principal (UI-supplied)'),
|
|
78895
|
+
});
|
|
78896
|
+
/** Access policy for a service universe (per-user and per-group grants). */
|
|
78897
|
+
const ServiceUniverseAccessSchema = v4.z.looseObject({
|
|
78898
|
+
users: v4.z
|
|
78899
|
+
.array(ServiceUniverseAccessEntrySchema)
|
|
78900
|
+
.optional()
|
|
78901
|
+
.describe('Per-user access grants'),
|
|
78902
|
+
groups: v4.z
|
|
78903
|
+
.array(ServiceUniverseAccessEntrySchema)
|
|
78904
|
+
.optional()
|
|
78905
|
+
.describe('Per-group access grants'),
|
|
78906
|
+
});
|
|
78907
|
+
/** The TAS scoping of a service universe — its inner filter selects the services. */
|
|
78908
|
+
const ServiceUniverseTasSpecSchema = v4.z.looseObject({
|
|
78909
|
+
filter: TasFilterSchema.optional().describe('TAS filter selecting the topology; for a service universe this is a SERVICE-op filter over the chosen service names'),
|
|
78910
|
+
includeMetricFilter: v4.z
|
|
78911
|
+
.boolean()
|
|
78912
|
+
.optional()
|
|
78913
|
+
.describe('Whether a metric filter is applied; defaults to false'),
|
|
78914
|
+
projection: v4.z
|
|
78915
|
+
.string()
|
|
78916
|
+
.optional()
|
|
78917
|
+
.describe("Projection mode, e.g. 'NO_TYPE'"),
|
|
78918
|
+
projectionFilter: v4.z
|
|
78919
|
+
.looseObject({
|
|
78920
|
+
attributes: v4.z
|
|
78921
|
+
.array(v4.z.string())
|
|
78922
|
+
.optional()
|
|
78923
|
+
.describe('Attributes to project, e.g. ["name"]'),
|
|
78924
|
+
})
|
|
78925
|
+
.optional()
|
|
78926
|
+
.describe('Which vertex attributes to project'),
|
|
78927
|
+
});
|
|
78928
|
+
/** A TAS sub-view of a service universe. */
|
|
78929
|
+
const ServiceUniverseTasViewSchema = v4.z.looseObject({
|
|
78930
|
+
type: v4.z.literal('tas').describe('Discriminator: topology (TAS) sub-view'),
|
|
78931
|
+
filter: ServiceUniverseTasSpecSchema.describe('TAS scoping for this sub-view'),
|
|
78932
|
+
permissions: v4.z
|
|
78933
|
+
.array(ServiceUniversePermissionSchema)
|
|
78934
|
+
.optional()
|
|
78935
|
+
.describe('Permissions on this sub-view'),
|
|
78936
|
+
});
|
|
78937
|
+
/** A NASS (metrics) sub-view of a service universe. */
|
|
78938
|
+
const ServiceUniverseNassViewSchema = v4.z.looseObject({
|
|
78939
|
+
type: v4.z.literal('nass').describe('Discriminator: metrics (NASS) sub-view'),
|
|
78940
|
+
filter: v4.z
|
|
78941
|
+
.looseObject({
|
|
78942
|
+
filter: v4.z
|
|
78943
|
+
.looseObject({ op: v4.z.string().describe('Filter op, e.g. "ALL"') })
|
|
78944
|
+
.optional()
|
|
78945
|
+
.describe('NASS filter tree'),
|
|
78946
|
+
serviceFilter: v4.z
|
|
78947
|
+
.unknown()
|
|
78948
|
+
.optional()
|
|
78949
|
+
.describe('Optional service-scoped NASS filter'),
|
|
78950
|
+
})
|
|
78951
|
+
.optional()
|
|
78952
|
+
.describe('NASS scoping for this sub-view'),
|
|
78953
|
+
permissions: v4.z
|
|
78954
|
+
.array(ServiceUniversePermissionSchema)
|
|
78955
|
+
.optional()
|
|
78956
|
+
.describe('Permissions on this sub-view'),
|
|
78957
|
+
});
|
|
78958
|
+
/** A service universe sub-view: TAS (service-scoped topology) or NASS (metrics). */
|
|
78959
|
+
const ServiceUniverseViewSchema = v4.z
|
|
78960
|
+
.discriminatedUnion('type', [
|
|
78961
|
+
ServiceUniverseTasViewSchema,
|
|
78962
|
+
ServiceUniverseNassViewSchema,
|
|
78963
|
+
])
|
|
78964
|
+
.describe('A TAS or NASS sub-view of the universe');
|
|
78965
|
+
/** Metadata for a service universe. */
|
|
78966
|
+
const ServiceUniverseAttributesSchema = v4.z.looseObject({
|
|
78967
|
+
owner: v4.z
|
|
78968
|
+
.string()
|
|
78969
|
+
.optional()
|
|
78970
|
+
.describe("Owning product; service universes are 'OI'"),
|
|
78971
|
+
label: v4.z.string().describe('Human-readable universe name'),
|
|
78972
|
+
description: v4.z.string().optional().describe('Universe description'),
|
|
78973
|
+
});
|
|
78974
|
+
/**
|
|
78975
|
+
* The create/update request body for a service universe
|
|
78976
|
+
* (`POST/PUT views/view`).
|
|
78977
|
+
*/
|
|
78978
|
+
const ServiceUniverseBodySchema = v4.z.looseObject({
|
|
78979
|
+
inactive: v4.z
|
|
78980
|
+
.boolean()
|
|
78981
|
+
.optional()
|
|
78982
|
+
.describe('Whether the universe is inactive (hidden from active use)'),
|
|
78983
|
+
attributes: ServiceUniverseAttributesSchema.describe('Universe metadata (owner, label, description)'),
|
|
78984
|
+
access: ServiceUniverseAccessSchema.optional().describe('Access policy (users/groups)'),
|
|
78985
|
+
views: v4.z
|
|
78986
|
+
.array(ServiceUniverseViewSchema)
|
|
78987
|
+
.describe('The TAS + NASS sub-views defining the universe scope'),
|
|
78988
|
+
});
|
|
78989
|
+
/**
|
|
78990
|
+
* A summary row from `GET oi/v2/universe/getAllUniverseList`.
|
|
78991
|
+
*/
|
|
78992
|
+
const ServiceUniverseListItemSchema = v4.z.looseObject({
|
|
78993
|
+
id: v4.z.string().describe('Universe (view) id, e.g. "VIEW1821"'),
|
|
78994
|
+
name: v4.z.string().optional().describe('Universe label'),
|
|
78995
|
+
description: v4.z.string().optional().describe('Universe description'),
|
|
78996
|
+
owner: v4.z
|
|
78997
|
+
.string()
|
|
78998
|
+
.optional()
|
|
78999
|
+
.describe("Owning product; service universes are 'OI'"),
|
|
79000
|
+
inactive: v4.z.boolean().optional().describe('Whether the universe is inactive'),
|
|
79001
|
+
});
|
|
79002
|
+
const ServiceUniverseListResponseSchema = v4.z
|
|
79003
|
+
.array(ServiceUniverseListItemSchema)
|
|
79004
|
+
.describe('All universes returned by getAllUniverseList');
|
|
79005
|
+
/**
|
|
79006
|
+
* A full service universe as returned by `POST views/queryView` (op:ID).
|
|
79007
|
+
* Loosely modeled — the detail response echoes the body plus server-resolved
|
|
79008
|
+
* fields (viewId, resolved access).
|
|
79009
|
+
*/
|
|
79010
|
+
const ServiceUniverseSchema = v4.z.looseObject({
|
|
79011
|
+
viewId: v4.z.string().optional().describe('Server-assigned view id'),
|
|
79012
|
+
inactive: v4.z.boolean().optional().describe('Whether the universe is inactive'),
|
|
79013
|
+
attributes: ServiceUniverseAttributesSchema.optional().describe('Universe metadata'),
|
|
79014
|
+
access: ServiceUniverseAccessSchema.optional().describe('Access policy'),
|
|
79015
|
+
views: v4.z
|
|
79016
|
+
.array(v4.z.unknown())
|
|
79017
|
+
.optional()
|
|
79018
|
+
.describe('The universe sub-views (TAS/NASS)'),
|
|
79019
|
+
});
|
|
79020
|
+
/** Response wrapper for `POST views/queryView`. */
|
|
79021
|
+
const ServiceUniverseQueryResponseSchema = v4.z.looseObject({
|
|
79022
|
+
views: v4.z
|
|
79023
|
+
.array(ServiceUniverseSchema)
|
|
79024
|
+
.optional()
|
|
79025
|
+
.describe('Universes matching the query filter'),
|
|
79026
|
+
});
|
|
79027
|
+
|
|
79028
|
+
/**
|
|
79029
|
+
* Service for **Service Universes** — OI-owned universes (views) scoped to a
|
|
79030
|
+
* curated list of services, managed via the tenant `views/*` API plus the OI
|
|
79031
|
+
* universe-list endpoint. The "View sample" preview runs the SERVICE-op filter
|
|
79032
|
+
* as a TAS graph query.
|
|
79033
|
+
*
|
|
79034
|
+
* @example
|
|
79035
|
+
* ```ts
|
|
79036
|
+
* const svc = new ServiceUniverseService(dxSaasService, log);
|
|
79037
|
+
* const body = ServiceUniverseService.buildBody({ label: 'IoT', serviceNames: ['IoT RDS'] });
|
|
79038
|
+
* await svc.createServiceUniverse(body);
|
|
79039
|
+
* ```
|
|
79040
|
+
*/
|
|
79041
|
+
class ServiceUniverseService {
|
|
79042
|
+
dxSaasService;
|
|
79043
|
+
log;
|
|
79044
|
+
constructor(dxSaasService, log) {
|
|
79045
|
+
this.dxSaasService = dxSaasService;
|
|
79046
|
+
this.log = log;
|
|
79047
|
+
}
|
|
79048
|
+
/**
|
|
79049
|
+
* Lists service universes (OI-owned) via
|
|
79050
|
+
* `GET oi/v2/universe/getAllUniverseList` (VIEWALL).
|
|
79051
|
+
*
|
|
79052
|
+
* @remarks Response-validated (mismatch → `log.warn`, raw still returned).
|
|
79053
|
+
*/
|
|
79054
|
+
async listServiceUniverses() {
|
|
79055
|
+
const raw = await this.dxSaasService.oiGet('oi/v2/universe/getAllUniverseList');
|
|
79056
|
+
const result = ServiceUniverseListResponseSchema.safeParse(raw);
|
|
79057
|
+
if (!result.success) {
|
|
79058
|
+
this.log.warn('ServiceUniverseListResponse validation warning:', result.error.message);
|
|
79059
|
+
}
|
|
79060
|
+
const all = raw ?? [];
|
|
79061
|
+
return all.filter((u) => (u.owner ?? '').toUpperCase() === 'OI');
|
|
79062
|
+
}
|
|
79063
|
+
/**
|
|
79064
|
+
* Returns a single service universe by its view id via
|
|
79065
|
+
* `POST views/queryView` (op:ID). Throws if not exactly one match.
|
|
79066
|
+
*/
|
|
79067
|
+
async getServiceUniverse(viewId) {
|
|
79068
|
+
const raw = await this.dxSaasService.tenantPost('views/queryView', {
|
|
79069
|
+
filter: { op: 'ID', ids: [viewId], input: { op: 'ALL' } },
|
|
79070
|
+
includeInactive: true,
|
|
79071
|
+
});
|
|
79072
|
+
const result = ServiceUniverseQueryResponseSchema.safeParse(raw);
|
|
79073
|
+
if (!result.success) {
|
|
79074
|
+
this.log.warn('ServiceUniverseQueryResponse validation warning:', result.error.message);
|
|
79075
|
+
}
|
|
79076
|
+
const views = raw.views ?? [];
|
|
79077
|
+
if (views.length !== 1) {
|
|
79078
|
+
throw new Error(`Expected exactly one universe with viewId '${viewId}', found ${views.length}.`);
|
|
79079
|
+
}
|
|
79080
|
+
return views[0];
|
|
79081
|
+
}
|
|
79082
|
+
/**
|
|
79083
|
+
* Creates a service universe via `POST views/view`.
|
|
79084
|
+
*
|
|
79085
|
+
* @throws ZodError if the body fails schema validation.
|
|
79086
|
+
*/
|
|
79087
|
+
async createServiceUniverse(body) {
|
|
79088
|
+
ServiceUniverseBodySchema.parse(body);
|
|
79089
|
+
return this.dxSaasService.tenantPost('views/view', body);
|
|
79090
|
+
}
|
|
79091
|
+
/**
|
|
79092
|
+
* Updates a service universe via `PUT views/view/{viewId}`.
|
|
79093
|
+
*
|
|
79094
|
+
* @throws ZodError if the body fails schema validation.
|
|
79095
|
+
*/
|
|
79096
|
+
async updateServiceUniverse(viewId, body) {
|
|
79097
|
+
ServiceUniverseBodySchema.parse(body);
|
|
79098
|
+
return this.dxSaasService.tenantPut(`views/view/${encodeURIComponent(viewId)}`, body);
|
|
79099
|
+
}
|
|
79100
|
+
/**
|
|
79101
|
+
* PUTs a full universe body to `views/view/{viewId}` **without** re-validating
|
|
79102
|
+
* it against the client schema. Used by the access commands, which round-trip
|
|
79103
|
+
* the universe's existing views verbatim (a UI-created universe may carry view
|
|
79104
|
+
* shapes our builder wouldn't reconstruct); only the `access` policy changes.
|
|
79105
|
+
*/
|
|
79106
|
+
async putServiceUniverse(viewId, body) {
|
|
79107
|
+
return this.dxSaasService.tenantPut(`views/view/${encodeURIComponent(viewId)}`, body);
|
|
79108
|
+
}
|
|
79109
|
+
/** Deletes a service universe via `DELETE views/view/{viewId}`. */
|
|
79110
|
+
async deleteServiceUniverse(viewId) {
|
|
79111
|
+
return this.dxSaasService.tenantDelete(`views/view/${encodeURIComponent(viewId)}`);
|
|
79112
|
+
}
|
|
79113
|
+
/**
|
|
79114
|
+
* Checks which of the given service names actually exist on the tenant, via
|
|
79115
|
+
* `GET oi/v2/sa/services/{name}` per name. A 404 means the service does not
|
|
79116
|
+
* exist; any other error is re-thrown (so a transient failure is never
|
|
79117
|
+
* silently reported as "missing").
|
|
79118
|
+
*
|
|
79119
|
+
* @throws Error if any name could not be verified for a non-404 reason.
|
|
79120
|
+
*/
|
|
79121
|
+
async resolveServiceNames(serviceNames) {
|
|
79122
|
+
const existing = [];
|
|
79123
|
+
const missing = [];
|
|
79124
|
+
const errored = [];
|
|
79125
|
+
await dist$1.PromisePool.for(serviceNames)
|
|
79126
|
+
.withConcurrency(5)
|
|
79127
|
+
.process(async (name) => {
|
|
79128
|
+
try {
|
|
79129
|
+
await this.dxSaasService.oiGet(`oi/v2/sa/services/${encodeURIComponent(name)}`, { subservices: false });
|
|
79130
|
+
existing.push(name);
|
|
79131
|
+
}
|
|
79132
|
+
catch (err) {
|
|
79133
|
+
if (err.httpCode === 404) {
|
|
79134
|
+
missing.push(name);
|
|
79135
|
+
}
|
|
79136
|
+
else {
|
|
79137
|
+
errored.push({ name, error: err });
|
|
79138
|
+
}
|
|
79139
|
+
}
|
|
79140
|
+
});
|
|
79141
|
+
if (errored.length > 0) {
|
|
79142
|
+
const first = errored[0].error;
|
|
79143
|
+
const reason = first instanceof Error ? first.message : String(first);
|
|
79144
|
+
throw new Error(`Could not verify ${errored.length} service name(s) (${errored
|
|
79145
|
+
.map((e) => e.name)
|
|
79146
|
+
.join(', ')}): ${reason}`);
|
|
79147
|
+
}
|
|
79148
|
+
return { existing, missing };
|
|
79149
|
+
}
|
|
79150
|
+
/**
|
|
79151
|
+
* "View sample": runs the SERVICE-scoped filter as a TAS graph query
|
|
79152
|
+
* (`POST tas/graph/query`) and returns the matching entities — the dry-run
|
|
79153
|
+
* preview of what a universe over `serviceNames` would contain.
|
|
79154
|
+
*
|
|
79155
|
+
* @remarks Response-validated (mismatch → `log.warn`, raw still returned).
|
|
79156
|
+
*/
|
|
79157
|
+
async sampleServices(serviceNames, options = {}) {
|
|
79158
|
+
const query = {
|
|
79159
|
+
filter: {
|
|
79160
|
+
op: 'OR',
|
|
79161
|
+
input: [
|
|
79162
|
+
{
|
|
79163
|
+
op: 'SERVICE',
|
|
79164
|
+
values: serviceNames,
|
|
79165
|
+
includeServiceHierarchy: options.includeServiceHierarchy ?? true,
|
|
79166
|
+
excludeSubServices: options.excludeSubServices ?? false,
|
|
79167
|
+
},
|
|
79168
|
+
],
|
|
79169
|
+
},
|
|
79170
|
+
authorizationView: 'VIEWALL',
|
|
79171
|
+
schema: 'SA',
|
|
79172
|
+
limit: options.limit ?? 200,
|
|
79173
|
+
};
|
|
79174
|
+
TasQuerySchema.parse(query);
|
|
79175
|
+
const raw = await this.dxSaasService.tenantPost('tas/graph/query', query);
|
|
79176
|
+
const result = TasGraphSchema.safeParse(raw);
|
|
79177
|
+
if (!result.success) {
|
|
79178
|
+
this.log.warn('ServiceUniverse sample (TasGraph) validation warning:', result.error.message);
|
|
79179
|
+
}
|
|
79180
|
+
return raw;
|
|
79181
|
+
}
|
|
79182
|
+
/**
|
|
79183
|
+
* Builds a service universe request body: a SERVICE-scoped TAS sub-view over
|
|
79184
|
+
* `serviceNames` (or an ALL view when empty) plus an ALL NASS sub-view. Mirrors
|
|
79185
|
+
* the shape the DXO2 Service Universe UI submits.
|
|
79186
|
+
*/
|
|
79187
|
+
/** A user/group access grant: READ+WRITE, no attribute scoping (per current UX). */
|
|
79188
|
+
static accessEntry(name, canManage) {
|
|
79189
|
+
return {
|
|
79190
|
+
name,
|
|
79191
|
+
permissions: ['READ', 'WRITE'],
|
|
79192
|
+
canManage,
|
|
79193
|
+
attributeInclude: [],
|
|
79194
|
+
attributeExclude: [],
|
|
79195
|
+
};
|
|
79196
|
+
}
|
|
79197
|
+
/**
|
|
79198
|
+
* Returns a copy of `existing` with a user/group access grant added (or
|
|
79199
|
+
* updated in place if the principal already has a grant). Preserves the
|
|
79200
|
+
* universe's server-managed fields and views verbatim — only `access`
|
|
79201
|
+
* changes. Pass exactly one of `user` / `group`.
|
|
79202
|
+
*/
|
|
79203
|
+
static withAccessEntry(existing, target) {
|
|
79204
|
+
const isGroup = !!target.group;
|
|
79205
|
+
const name = (target.user ?? target.group);
|
|
79206
|
+
const users = [...(existing.access?.users ?? [])];
|
|
79207
|
+
const groups = [...(existing.access?.groups ?? [])];
|
|
79208
|
+
const list = isGroup ? groups : users;
|
|
79209
|
+
const next = list.filter((e) => e.name !== name);
|
|
79210
|
+
next.push(this.accessEntry(name, target.canManage));
|
|
79211
|
+
const access = isGroup
|
|
79212
|
+
? { users, groups: next }
|
|
79213
|
+
: { users: next, groups };
|
|
79214
|
+
return {
|
|
79215
|
+
...existing,
|
|
79216
|
+
access,
|
|
79217
|
+
};
|
|
79218
|
+
}
|
|
79219
|
+
/**
|
|
79220
|
+
* Returns a copy of `existing` with a user/group access grant removed.
|
|
79221
|
+
* Preserves everything else verbatim. Pass exactly one of `user` / `group`.
|
|
79222
|
+
*/
|
|
79223
|
+
static withoutAccessEntry(existing, target) {
|
|
79224
|
+
const isGroup = !!target.group;
|
|
79225
|
+
const name = (target.user ?? target.group);
|
|
79226
|
+
const users = (existing.access?.users ?? []).filter((e) => isGroup || e.name !== name);
|
|
79227
|
+
const groups = (existing.access?.groups ?? []).filter((e) => !isGroup || e.name !== name);
|
|
79228
|
+
return {
|
|
79229
|
+
...existing,
|
|
79230
|
+
access: { users, groups },
|
|
79231
|
+
};
|
|
79232
|
+
}
|
|
79233
|
+
static buildBody(input) {
|
|
79234
|
+
const { label, description, serviceNames, inactive, includeServiceHierarchy, excludeSubServices, access, } = input;
|
|
79235
|
+
const tasFilter = serviceNames.length === 0
|
|
79236
|
+
? { op: 'ALL' }
|
|
79237
|
+
: {
|
|
79238
|
+
op: 'SERVICE',
|
|
79239
|
+
values: serviceNames,
|
|
79240
|
+
includeServiceHierarchy: includeServiceHierarchy ?? true,
|
|
79241
|
+
excludeSubServices: excludeSubServices ?? false,
|
|
79242
|
+
};
|
|
79243
|
+
return {
|
|
79244
|
+
inactive: inactive ?? false,
|
|
79245
|
+
attributes: { owner: 'OI', label, description: description ?? '' },
|
|
79246
|
+
access: access ?? { groups: [], users: [] },
|
|
79247
|
+
views: [
|
|
79248
|
+
{
|
|
79249
|
+
type: 'tas',
|
|
79250
|
+
filter: {
|
|
79251
|
+
filter: tasFilter,
|
|
79252
|
+
includeMetricFilter: false,
|
|
79253
|
+
projection: 'NO_TYPE',
|
|
79254
|
+
projectionFilter: { attributes: ['name'] },
|
|
79255
|
+
},
|
|
79256
|
+
permissions: ['READ', 'WRITE'],
|
|
79257
|
+
},
|
|
79258
|
+
{
|
|
79259
|
+
type: 'nass',
|
|
79260
|
+
filter: {
|
|
79261
|
+
filter: { op: 'ALL' },
|
|
79262
|
+
serviceFilter: serviceNames.length === 0
|
|
79263
|
+
? undefined
|
|
79264
|
+
: { op: 'SERVICE', values: serviceNames },
|
|
79265
|
+
},
|
|
79266
|
+
permissions: ['READ', 'WRITE'],
|
|
79267
|
+
},
|
|
79268
|
+
],
|
|
79269
|
+
};
|
|
79270
|
+
}
|
|
79271
|
+
}
|
|
79272
|
+
|
|
78834
79273
|
var NullSimpleLog = Logging.NullSimpleLog;
|
|
78835
79274
|
/**
|
|
78836
79275
|
* Creates and returns a container of all DX client services (ACC, alerts, alarms,
|
|
@@ -78865,6 +79304,7 @@ function createServiceMonolith(dxDoConfiguration, log = new NullSimpleLog()) {
|
|
|
78865
79304
|
dxDataStoreNASSService: new DataStoreNASSService(dxSaaSService, log),
|
|
78866
79305
|
dxDataStoreNASSQLService: new DataStoreNASSQLService(dxSaaSService, log),
|
|
78867
79306
|
dxDataStoreTASService: new DataStoreTASService(dxSaaSService, log),
|
|
79307
|
+
dxServiceUniverseService: new ServiceUniverseService(dxSaaSService, log),
|
|
78868
79308
|
dxEventService: new EventService(dxSaaSService, log),
|
|
78869
79309
|
dxExperienceService: new ExperienceService(dxSaaSService, log),
|
|
78870
79310
|
dxFeaturesService: new DataStoreFeaturesService(dxSaaSService, log),
|
|
@@ -79069,6 +79509,7 @@ exports.SLIService = SLIService;
|
|
|
79069
79509
|
exports.SQLService = SQLService;
|
|
79070
79510
|
exports.ServiceFilterSpecifierSchema = ServiceFilterSpecifierSchema;
|
|
79071
79511
|
exports.ServiceService = ServiceService;
|
|
79512
|
+
exports.ServiceUniverseService = ServiceUniverseService;
|
|
79072
79513
|
exports.SessionService = SessionService;
|
|
79073
79514
|
exports.SimpleHTTPError = SimpleHTTPError;
|
|
79074
79515
|
exports.SituationService = SituationService;
|