feat(device-core): expose safe project control views
This commit is contained in:
@@ -50,6 +50,10 @@ The standalone Hub application `Device Core` / `Device Manager` is the human
|
|||||||
control-plane shell. Its server-owned BFF calls the disabled-by-default
|
control-plane shell. Its server-owned BFF calls the disabled-by-default
|
||||||
management and query API in `device-control-core`; browsers never receive the
|
management and query API in `device-control-core`; browsers never receive the
|
||||||
Core bearer token and never author actor, role, group or owner-scope headers.
|
Core bearer token and never author actor, role, group or owner-scope headers.
|
||||||
|
The authorized project workspace exposes only bounded metadata for catalog,
|
||||||
|
routes/sessions, bindings, configuration state, command state, audit and access;
|
||||||
|
raw audit/configuration payloads, command parameters/transport refs, external
|
||||||
|
approval proofs, credential refs and restricted identifier digests remain in Core.
|
||||||
Foundry remains a downstream consumer for project-approved device data and is
|
Foundry remains a downstream consumer for project-approved device data and is
|
||||||
not the device registry or administration boundary.
|
not the device registry or administration boundary.
|
||||||
|
|
||||||
|
|||||||
@@ -127,12 +127,222 @@ export async function getDeviceProjectWorkspace(client, actor, projectId) {
|
|||||||
[projectId],
|
[projectId],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const capabilities = new Set(access.capabilities);
|
||||||
|
const mayManageRoutes = capabilities.has("route.manage");
|
||||||
|
const catalogParameters = [projectId, mayManageRoutes];
|
||||||
|
const adapterPackages = await client.query(
|
||||||
|
`select ap.id, ap.package_key, ap.display_name, ap.publisher_ref,
|
||||||
|
ap.lifecycle_state, ap.created_at, ap.updated_at
|
||||||
|
from device_adapter_packages ap
|
||||||
|
where $2::boolean
|
||||||
|
or exists (
|
||||||
|
select 1
|
||||||
|
from device_adapter_versions av
|
||||||
|
join device_model_profiles dmp on dmp.adapter_version_id = av.id
|
||||||
|
where av.adapter_package_id = ap.id
|
||||||
|
and (
|
||||||
|
exists (
|
||||||
|
select 1 from device_routes dr
|
||||||
|
where dr.project_id = $1 and dr.model_profile_ref = dmp.profile_ref
|
||||||
|
)
|
||||||
|
or exists (
|
||||||
|
select 1 from device_instances di
|
||||||
|
where di.project_id = $1 and di.model_profile_ref = dmp.profile_ref
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
order by ap.display_name, ap.id`,
|
||||||
|
catalogParameters,
|
||||||
|
);
|
||||||
|
const adapterVersions = await client.query(
|
||||||
|
`select av.id, av.adapter_package_id, av.version,
|
||||||
|
av.runtime_package_ref, av.content_digest, av.contract_version,
|
||||||
|
av.capabilities, av.lifecycle_state, av.created_at, av.updated_at
|
||||||
|
from device_adapter_versions av
|
||||||
|
where $2::boolean
|
||||||
|
or exists (
|
||||||
|
select 1 from device_model_profiles dmp
|
||||||
|
where dmp.adapter_version_id = av.id
|
||||||
|
and (
|
||||||
|
exists (
|
||||||
|
select 1 from device_routes dr
|
||||||
|
where dr.project_id = $1 and dr.model_profile_ref = dmp.profile_ref
|
||||||
|
)
|
||||||
|
or exists (
|
||||||
|
select 1 from device_instances di
|
||||||
|
where di.project_id = $1 and di.model_profile_ref = dmp.profile_ref
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
order by av.adapter_package_id, av.created_at, av.id`,
|
||||||
|
catalogParameters,
|
||||||
|
);
|
||||||
|
const modelProfiles = await client.query(
|
||||||
|
`select dmp.profile_ref, dmp.adapter_version_id, dmp.schema_version,
|
||||||
|
dmp.vendor, dmp.model, dmp.device_type, dmp.protocol,
|
||||||
|
dmp.schema_artifact_ref, dmp.profile_digest, dmp.capabilities,
|
||||||
|
dmp.lifecycle_state, dmp.created_at, dmp.updated_at
|
||||||
|
from device_model_profiles dmp
|
||||||
|
where $2::boolean
|
||||||
|
or exists (
|
||||||
|
select 1 from device_routes dr
|
||||||
|
where dr.project_id = $1 and dr.model_profile_ref = dmp.profile_ref
|
||||||
|
)
|
||||||
|
or exists (
|
||||||
|
select 1 from device_instances di
|
||||||
|
where di.project_id = $1 and di.model_profile_ref = dmp.profile_ref
|
||||||
|
)
|
||||||
|
order by dmp.vendor, dmp.model, dmp.profile_ref`,
|
||||||
|
catalogParameters,
|
||||||
|
);
|
||||||
|
const edges = await client.query(
|
||||||
|
`select de.id, de.edge_key, de.display_name, de.deployment_ref,
|
||||||
|
de.lifecycle_state, de.created_at, de.updated_at
|
||||||
|
from device_edges de
|
||||||
|
where $2::boolean
|
||||||
|
or exists (
|
||||||
|
select 1 from device_routes dr
|
||||||
|
where dr.project_id = $1 and dr.edge_id = de.id
|
||||||
|
)
|
||||||
|
order by de.display_name, de.id`,
|
||||||
|
catalogParameters,
|
||||||
|
);
|
||||||
|
const routes = await client.query(
|
||||||
|
`select dr.id, dr.route_key, dr.display_name, dr.edge_id,
|
||||||
|
de.display_name as edge_name, dr.model_profile_ref,
|
||||||
|
dmp.vendor as profile_vendor, dmp.model as profile_model,
|
||||||
|
dr.listener_ref, dr.protocol, dr.direction, dr.lifecycle_state,
|
||||||
|
dr.created_at, dr.updated_at,
|
||||||
|
count(ds.id)::bigint as session_count,
|
||||||
|
(count(ds.id) filter (
|
||||||
|
where ds.lifecycle_state in ('connecting', 'online')
|
||||||
|
))::bigint as active_session_count
|
||||||
|
from device_routes dr
|
||||||
|
join device_edges de on de.id = dr.edge_id
|
||||||
|
join device_model_profiles dmp on dmp.profile_ref = dr.model_profile_ref
|
||||||
|
left join device_sessions ds on ds.route_id = dr.id
|
||||||
|
where dr.project_id = $1
|
||||||
|
group by dr.id, de.display_name, dmp.vendor, dmp.model
|
||||||
|
order by dr.display_name, dr.id`,
|
||||||
|
[projectId],
|
||||||
|
);
|
||||||
|
const sessions = capabilities.has("telemetry.observe")
|
||||||
|
? await client.query(
|
||||||
|
`select ds.id, ds.route_id, dr.display_name as route_name,
|
||||||
|
ds.device_id, di.display_name as device_name,
|
||||||
|
ds.protocol, ds.lifecycle_state, ds.connected_at,
|
||||||
|
ds.last_seen_at, ds.disconnected_at, ds.close_reason_code,
|
||||||
|
ds.frame_count, ds.byte_count
|
||||||
|
from device_sessions ds
|
||||||
|
join device_routes dr on dr.id = ds.route_id
|
||||||
|
left join device_instances di on di.id = ds.device_id
|
||||||
|
where ds.project_id = $1
|
||||||
|
order by ds.last_seen_at desc, ds.id
|
||||||
|
limit 200`,
|
||||||
|
[projectId],
|
||||||
|
)
|
||||||
|
: { rows: [] };
|
||||||
|
const bindings = capabilities.has("binding.manage")
|
||||||
|
? await client.query(
|
||||||
|
`select drb.id, drb.binding_key, drb.display_name,
|
||||||
|
drb.source_kind, drb.device_id, drb.collection_id,
|
||||||
|
coalesce(di.display_name, dc.name) as source_name,
|
||||||
|
drb.target_kind, drb.target_ref, drb.capabilities,
|
||||||
|
drb.lifecycle_state, drb.source_approved_at,
|
||||||
|
drb.created_at, drb.updated_at
|
||||||
|
from device_resource_bindings drb
|
||||||
|
left join device_instances di on di.id = drb.device_id
|
||||||
|
left join device_collections dc on dc.id = drb.collection_id
|
||||||
|
where drb.project_id = $1
|
||||||
|
order by drb.updated_at desc, drb.id`,
|
||||||
|
[projectId],
|
||||||
|
)
|
||||||
|
: { rows: [] };
|
||||||
|
const configurationRevisions = capabilities.has("configuration.read")
|
||||||
|
? await client.query(
|
||||||
|
`select dcr.id, dcr.device_id, di.display_name as device_name,
|
||||||
|
dcr.revision_number, dcr.model_profile_ref,
|
||||||
|
dcr.schema_artifact_ref, dcr.configuration_digest,
|
||||||
|
dcr.change_summary, dcr.created_at
|
||||||
|
from device_configuration_revisions dcr
|
||||||
|
join device_instances di on di.id = dcr.device_id
|
||||||
|
where dcr.project_id = $1
|
||||||
|
order by dcr.created_at desc, dcr.id
|
||||||
|
limit 200`,
|
||||||
|
[projectId],
|
||||||
|
)
|
||||||
|
: { rows: [] };
|
||||||
|
const configurationStates = capabilities.has("configuration.read")
|
||||||
|
? await client.query(
|
||||||
|
`select dcs.device_id, di.display_name as device_name,
|
||||||
|
dcs.desired_revision_id, dcs.applied_revision_id,
|
||||||
|
dcs.applied_at, dcs.updated_at
|
||||||
|
from device_configuration_state dcs
|
||||||
|
join device_instances di on di.id = dcs.device_id
|
||||||
|
where dcs.project_id = $1
|
||||||
|
order by di.display_name, dcs.device_id`,
|
||||||
|
[projectId],
|
||||||
|
)
|
||||||
|
: { rows: [] };
|
||||||
|
const mayReadCommands = ["command.plan", "command.confirm", "command.dispatch"]
|
||||||
|
.some((capability) => capabilities.has(capability));
|
||||||
|
const commands = mayReadCommands
|
||||||
|
? await client.query(
|
||||||
|
`select dc.id, dc.device_id, di.display_name as device_name,
|
||||||
|
dc.command_key, dc.command_catalog_ref, dc.command_type,
|
||||||
|
dc.risk_class, dc.lifecycle_state, dc.planned_at, dc.expires_at,
|
||||||
|
dc.confirmed_at, dc.dispatched_at, dc.acknowledged_at,
|
||||||
|
dc.terminal_at, dc.terminal_reason_code,
|
||||||
|
dc.created_at, dc.updated_at
|
||||||
|
from device_commands dc
|
||||||
|
join device_instances di on di.id = dc.device_id
|
||||||
|
where dc.project_id = $1
|
||||||
|
order by dc.updated_at desc, dc.id
|
||||||
|
limit 200`,
|
||||||
|
[projectId],
|
||||||
|
)
|
||||||
|
: { rows: [] };
|
||||||
|
const auditEvents = capabilities.has("audit.read")
|
||||||
|
? await client.query(
|
||||||
|
`select dae.id, dae.event_type, dae.actor_ref,
|
||||||
|
dae.device_id, dae.discovery_id, dae.occurred_at
|
||||||
|
from device_audit_events dae
|
||||||
|
where dae.project_id = $1
|
||||||
|
order by dae.occurred_at desc, dae.id
|
||||||
|
limit 300`,
|
||||||
|
[projectId],
|
||||||
|
)
|
||||||
|
: { rows: [] };
|
||||||
|
const projectGrants = capabilities.has("access.manage")
|
||||||
|
? grantsResult
|
||||||
|
: { rows: [] };
|
||||||
|
|
||||||
return {
|
return {
|
||||||
project: projectSummaryView(project, access),
|
project: projectSummaryView(project, access),
|
||||||
devices: devices.rows.map(deviceView),
|
devices: devices.rows.map(deviceView),
|
||||||
collections: collections.rows.map(collectionView),
|
collections: collections.rows.map(collectionView),
|
||||||
discoveries: discoveries.rows.map(discoveryView),
|
discoveries: discoveries.rows.map(discoveryView),
|
||||||
enrollments: enrollments.rows.map(enrollmentView),
|
enrollments: enrollments.rows.map(enrollmentView),
|
||||||
|
adapterPackages: adapterPackages.rows.map(adapterPackageView),
|
||||||
|
adapterVersions: adapterVersions.rows.map(adapterVersionView),
|
||||||
|
modelProfiles: modelProfiles.rows.map(modelProfileView),
|
||||||
|
edges: edges.rows.map(edgeView),
|
||||||
|
routes: routes.rows.map(routeView),
|
||||||
|
sessions: sessions.rows.map(sessionView),
|
||||||
|
bindings: bindings.rows.map(bindingView),
|
||||||
|
configurationRevisions: configurationRevisions.rows.map(
|
||||||
|
configurationRevisionView,
|
||||||
|
),
|
||||||
|
configurationStates: configurationStates.rows.map(configurationStateView),
|
||||||
|
commands: commands.rows.map(commandView),
|
||||||
|
auditEvents: auditEvents.rows.map(auditEventView),
|
||||||
|
grants: projectGrants.rows.map(storedGrantView),
|
||||||
|
policies: {
|
||||||
|
commandTransport: "disabled",
|
||||||
|
commandPlanningApi: "disabled",
|
||||||
|
identifierProjection: "masked-only",
|
||||||
|
auditPayloadProjection: "metadata-only",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,7 +412,7 @@ function discoveryView(row) {
|
|||||||
protocol: row.protocol,
|
protocol: row.protocol,
|
||||||
lifecycleState: row.lifecycle_state,
|
lifecycleState: row.lifecycle_state,
|
||||||
enrollmentIntentRef: row.enrollment_intent_id
|
enrollmentIntentRef: row.enrollment_intent_id
|
||||||
? `enrollment:${row.enrollment_intent_id}`
|
? `enrollment-intent:${row.enrollment_intent_id}`
|
||||||
: null,
|
: null,
|
||||||
claimedDeviceRef: row.claimed_device_id ? `device:${row.claimed_device_id}` : null,
|
claimedDeviceRef: row.claimed_device_id ? `device:${row.claimed_device_id}` : null,
|
||||||
firstObservedAt: toIso(row.first_observed_at),
|
firstObservedAt: toIso(row.first_observed_at),
|
||||||
@@ -212,7 +422,7 @@ function discoveryView(row) {
|
|||||||
|
|
||||||
function enrollmentView(row) {
|
function enrollmentView(row) {
|
||||||
return {
|
return {
|
||||||
enrollmentIntentRef: `enrollment:${row.id}`,
|
enrollmentIntentRef: `enrollment-intent:${row.id}`,
|
||||||
enrollmentKey: row.enrollment_key,
|
enrollmentKey: row.enrollment_key,
|
||||||
displayName: row.display_name,
|
displayName: row.display_name,
|
||||||
modelProfileRef: row.model_profile_ref,
|
modelProfileRef: row.model_profile_ref,
|
||||||
@@ -231,6 +441,186 @@ function enrollmentView(row) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function adapterPackageView(row) {
|
||||||
|
return {
|
||||||
|
adapterPackageRef: `adapter-package:${row.id}`,
|
||||||
|
packageKey: row.package_key,
|
||||||
|
displayName: row.display_name,
|
||||||
|
publisherRef: row.publisher_ref,
|
||||||
|
lifecycleState: row.lifecycle_state,
|
||||||
|
createdAt: toIso(row.created_at),
|
||||||
|
updatedAt: toIso(row.updated_at),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function adapterVersionView(row) {
|
||||||
|
return {
|
||||||
|
adapterVersionRef: `adapter-version:${row.id}`,
|
||||||
|
adapterPackageRef: `adapter-package:${row.adapter_package_id}`,
|
||||||
|
version: row.version,
|
||||||
|
runtimePackageRef: row.runtime_package_ref,
|
||||||
|
contentDigest: row.content_digest,
|
||||||
|
contractVersion: row.contract_version,
|
||||||
|
capabilities: [...(row.capabilities ?? [])].sort(),
|
||||||
|
lifecycleState: row.lifecycle_state,
|
||||||
|
createdAt: toIso(row.created_at),
|
||||||
|
updatedAt: toIso(row.updated_at),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function modelProfileView(row) {
|
||||||
|
return {
|
||||||
|
modelProfileRef: row.profile_ref,
|
||||||
|
adapterVersionRef: row.adapter_version_id
|
||||||
|
? `adapter-version:${row.adapter_version_id}`
|
||||||
|
: null,
|
||||||
|
schemaVersion: row.schema_version,
|
||||||
|
vendor: row.vendor,
|
||||||
|
model: row.model,
|
||||||
|
deviceType: row.device_type,
|
||||||
|
protocol: row.protocol,
|
||||||
|
schemaArtifactRef: row.schema_artifact_ref ?? null,
|
||||||
|
profileDigest: row.profile_digest ?? null,
|
||||||
|
capabilities: [...(row.capabilities ?? [])].sort(),
|
||||||
|
lifecycleState: row.lifecycle_state,
|
||||||
|
createdAt: toIso(row.created_at),
|
||||||
|
updatedAt: toIso(row.updated_at),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function edgeView(row) {
|
||||||
|
return {
|
||||||
|
edgeRef: `edge:${row.id}`,
|
||||||
|
edgeKey: row.edge_key,
|
||||||
|
displayName: row.display_name,
|
||||||
|
deploymentRef: row.deployment_ref ?? null,
|
||||||
|
lifecycleState: row.lifecycle_state,
|
||||||
|
createdAt: toIso(row.created_at),
|
||||||
|
updatedAt: toIso(row.updated_at),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function routeView(row) {
|
||||||
|
return {
|
||||||
|
routeRef: `route:${row.id}`,
|
||||||
|
routeKey: row.route_key,
|
||||||
|
displayName: row.display_name,
|
||||||
|
edgeRef: `edge:${row.edge_id}`,
|
||||||
|
edgeName: row.edge_name,
|
||||||
|
modelProfileRef: row.model_profile_ref,
|
||||||
|
profileName: `${row.profile_vendor} ${row.profile_model}`.trim(),
|
||||||
|
listenerRef: row.listener_ref,
|
||||||
|
protocol: row.protocol,
|
||||||
|
direction: row.direction,
|
||||||
|
lifecycleState: row.lifecycle_state,
|
||||||
|
sessionCount: numericCount(row.session_count),
|
||||||
|
activeSessionCount: numericCount(row.active_session_count),
|
||||||
|
createdAt: toIso(row.created_at),
|
||||||
|
updatedAt: toIso(row.updated_at),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function sessionView(row) {
|
||||||
|
return {
|
||||||
|
sessionRef: `session:${row.id}`,
|
||||||
|
routeRef: `route:${row.route_id}`,
|
||||||
|
routeName: row.route_name,
|
||||||
|
deviceRef: row.device_id ? `device:${row.device_id}` : null,
|
||||||
|
deviceName: row.device_name ?? null,
|
||||||
|
protocol: row.protocol,
|
||||||
|
lifecycleState: row.lifecycle_state,
|
||||||
|
connectedAt: toIso(row.connected_at),
|
||||||
|
lastSeenAt: toIso(row.last_seen_at),
|
||||||
|
disconnectedAt: toIso(row.disconnected_at),
|
||||||
|
closeReasonCode: row.close_reason_code ?? null,
|
||||||
|
frameCount: numericCount(row.frame_count),
|
||||||
|
byteCount: numericCount(row.byte_count),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindingView(row) {
|
||||||
|
return {
|
||||||
|
bindingRef: `binding:${row.id}`,
|
||||||
|
bindingKey: row.binding_key,
|
||||||
|
displayName: row.display_name,
|
||||||
|
source: {
|
||||||
|
kind: row.source_kind,
|
||||||
|
ref: row.source_kind === "device"
|
||||||
|
? `device:${row.device_id}`
|
||||||
|
: `collection:${row.collection_id}`,
|
||||||
|
displayName: row.source_name,
|
||||||
|
},
|
||||||
|
target: { kind: row.target_kind, ref: row.target_ref },
|
||||||
|
capabilities: [...(row.capabilities ?? [])].sort(),
|
||||||
|
lifecycleState: row.lifecycle_state,
|
||||||
|
sourceApprovedAt: toIso(row.source_approved_at),
|
||||||
|
createdAt: toIso(row.created_at),
|
||||||
|
updatedAt: toIso(row.updated_at),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function configurationRevisionView(row) {
|
||||||
|
return {
|
||||||
|
configurationRevisionRef: `configuration-revision:${row.id}`,
|
||||||
|
deviceRef: `device:${row.device_id}`,
|
||||||
|
deviceName: row.device_name,
|
||||||
|
revisionNumber: numericCount(row.revision_number),
|
||||||
|
modelProfileRef: row.model_profile_ref,
|
||||||
|
schemaArtifactRef: row.schema_artifact_ref,
|
||||||
|
configurationDigest: row.configuration_digest,
|
||||||
|
changeSummary: row.change_summary ?? null,
|
||||||
|
createdAt: toIso(row.created_at),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function configurationStateView(row) {
|
||||||
|
return {
|
||||||
|
deviceRef: `device:${row.device_id}`,
|
||||||
|
deviceName: row.device_name,
|
||||||
|
desiredConfigurationRevisionRef: row.desired_revision_id
|
||||||
|
? `configuration-revision:${row.desired_revision_id}`
|
||||||
|
: null,
|
||||||
|
appliedConfigurationRevisionRef: row.applied_revision_id
|
||||||
|
? `configuration-revision:${row.applied_revision_id}`
|
||||||
|
: null,
|
||||||
|
appliedAt: toIso(row.applied_at),
|
||||||
|
updatedAt: toIso(row.updated_at),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function commandView(row) {
|
||||||
|
return {
|
||||||
|
commandRef: `command:${row.id}`,
|
||||||
|
deviceRef: `device:${row.device_id}`,
|
||||||
|
deviceName: row.device_name,
|
||||||
|
commandKey: row.command_key,
|
||||||
|
commandCatalogRef: row.command_catalog_ref,
|
||||||
|
commandType: row.command_type,
|
||||||
|
riskClass: row.risk_class,
|
||||||
|
lifecycleState: row.lifecycle_state,
|
||||||
|
plannedAt: toIso(row.planned_at),
|
||||||
|
expiresAt: toIso(row.expires_at),
|
||||||
|
confirmedAt: toIso(row.confirmed_at),
|
||||||
|
dispatchedAt: toIso(row.dispatched_at),
|
||||||
|
acknowledgedAt: toIso(row.acknowledged_at),
|
||||||
|
terminalAt: toIso(row.terminal_at),
|
||||||
|
terminalReasonCode: row.terminal_reason_code ?? null,
|
||||||
|
createdAt: toIso(row.created_at),
|
||||||
|
updatedAt: toIso(row.updated_at),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function auditEventView(row) {
|
||||||
|
return {
|
||||||
|
auditEventRef: `audit-event:${row.id}`,
|
||||||
|
eventType: row.event_type,
|
||||||
|
actorRef: row.actor_ref,
|
||||||
|
deviceRef: row.device_id ? `device:${row.device_id}` : null,
|
||||||
|
discoveryRef: row.discovery_id ? `discovery:${row.discovery_id}` : null,
|
||||||
|
occurredAt: toIso(row.occurred_at),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function grantView(row) {
|
function grantView(row) {
|
||||||
return storedGrantView({
|
return storedGrantView({
|
||||||
id: row.grant_id,
|
id: row.grant_id,
|
||||||
|
|||||||
@@ -59,9 +59,26 @@ test("project workspace returns only masked identity projections", async () => {
|
|||||||
assert.equal(workspace.devices[0].identifier.masked, "***********0001");
|
assert.equal(workspace.devices[0].identifier.masked, "***********0001");
|
||||||
assert.equal(workspace.discoveries[0].identifier.masked, "***********0001");
|
assert.equal(workspace.discoveries[0].identifier.masked, "***********0001");
|
||||||
assert.equal(workspace.enrollments[0].expectedIdentifier.masked, "***********0001");
|
assert.equal(workspace.enrollments[0].expectedIdentifier.masked, "***********0001");
|
||||||
|
assert.equal(
|
||||||
|
workspace.enrollments[0].enrollmentIntentRef,
|
||||||
|
"enrollment-intent:77777777-7777-4777-8777-777777777777",
|
||||||
|
);
|
||||||
|
assert.equal(workspace.adapterPackages[0].packageKey, "generic-tracker");
|
||||||
|
assert.equal(workspace.modelProfiles[0].modelProfileRef, "vendor.model.v1");
|
||||||
|
assert.equal(workspace.routes[0].activeSessionCount, 1);
|
||||||
|
assert.equal(workspace.sessions[0].frameCount, 12);
|
||||||
|
assert.equal(workspace.bindings[0].lifecycleState, "pending_external_approval");
|
||||||
|
assert.equal(workspace.configurationRevisions[0].revisionNumber, 1);
|
||||||
|
assert.equal(workspace.commands[0].lifecycleState, "acknowledged");
|
||||||
|
assert.equal(workspace.auditEvents[0].eventType, "device.observed");
|
||||||
|
assert.equal(workspace.grants[0].principalRef, "user:device-admin");
|
||||||
|
assert.equal(workspace.policies.commandTransport, "disabled");
|
||||||
const serialized = JSON.stringify(workspace);
|
const serialized = JSON.stringify(workspace);
|
||||||
assert.equal(serialized.includes("hmac-sha256"), false);
|
assert.equal(serialized.includes("hmac-sha256"), false);
|
||||||
assert.equal(serialized.includes("ndc-credref"), false);
|
assert.equal(serialized.includes("ndc-credref"), false);
|
||||||
|
assert.equal(serialized.includes("transport-message-secret"), false);
|
||||||
|
assert.equal(serialized.includes("external-approval-proof"), false);
|
||||||
|
assert.equal(serialized.includes("raw-audit-payload"), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("project read source never selects identifier digests or credential refs", async () => {
|
test("project read source never selects identifier digests or credential refs", async () => {
|
||||||
@@ -71,8 +88,9 @@ test("project read source never selects identifier digests or credential refs",
|
|||||||
);
|
);
|
||||||
assert.doesNotMatch(
|
assert.doesNotMatch(
|
||||||
source,
|
source,
|
||||||
/\b(?:identifier_digest|expected_identifier_digest|credential_ref)\b/,
|
/\b(?:identifier_digest|expected_identifier_digest|credential_ref|parameters_digest|parameters_projection|transport_message_ref|external_approval_ref|external_approval_digest)\b/,
|
||||||
);
|
);
|
||||||
|
assert.doesNotMatch(source, /\b(?:dae\.payload|dcr\.configuration)\b/);
|
||||||
});
|
});
|
||||||
|
|
||||||
function workspaceClient() {
|
function workspaceClient() {
|
||||||
@@ -86,7 +104,174 @@ function workspaceClient() {
|
|||||||
grantReads += 1;
|
grantReads += 1;
|
||||||
return { rows: [storedGrantRow()] };
|
return { rows: [storedGrantRow()] };
|
||||||
}
|
}
|
||||||
if (/from device_instances di/.test(sql)) {
|
if (/from device_adapter_packages ap/.test(sql)) {
|
||||||
|
return { rows: [{
|
||||||
|
id: "99999999-9999-4999-8999-999999999999",
|
||||||
|
package_key: "generic-tracker",
|
||||||
|
display_name: "Generic tracker",
|
||||||
|
publisher_ref: "publisher:nodedc",
|
||||||
|
lifecycle_state: "active",
|
||||||
|
created_at: timestamp,
|
||||||
|
updated_at: timestamp,
|
||||||
|
}] };
|
||||||
|
}
|
||||||
|
if (/from device_adapter_versions av/.test(sql)) {
|
||||||
|
return { rows: [{
|
||||||
|
id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
||||||
|
adapter_package_id: "99999999-9999-4999-8999-999999999999",
|
||||||
|
version: "1.0.0",
|
||||||
|
runtime_package_ref: "artifact:generic-tracker:1.0.0",
|
||||||
|
content_digest: `sha256:${"a".repeat(64)}`,
|
||||||
|
contract_version: "device-adapter.v1",
|
||||||
|
capabilities: ["telemetry"],
|
||||||
|
lifecycle_state: "active",
|
||||||
|
created_at: timestamp,
|
||||||
|
updated_at: timestamp,
|
||||||
|
}] };
|
||||||
|
}
|
||||||
|
if (/from device_model_profiles dmp/.test(sql)) {
|
||||||
|
return { rows: [{
|
||||||
|
profile_ref: "vendor.model.v1",
|
||||||
|
adapter_version_id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
||||||
|
schema_version: "1.0.0",
|
||||||
|
vendor: "Vendor",
|
||||||
|
model: "Model",
|
||||||
|
device_type: "tracker",
|
||||||
|
protocol: "INTERNAL",
|
||||||
|
schema_artifact_ref: "schema:vendor.model.v1",
|
||||||
|
profile_digest: `sha256:${"b".repeat(64)}`,
|
||||||
|
capabilities: ["telemetry"],
|
||||||
|
lifecycle_state: "active",
|
||||||
|
created_at: timestamp,
|
||||||
|
updated_at: timestamp,
|
||||||
|
}] };
|
||||||
|
}
|
||||||
|
if (/from device_edges de/.test(sql)) {
|
||||||
|
return { rows: [{
|
||||||
|
id: "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
|
||||||
|
edge_key: "edge-one",
|
||||||
|
display_name: "Edge one",
|
||||||
|
deployment_ref: "deployment:edge-one",
|
||||||
|
lifecycle_state: "active",
|
||||||
|
created_at: timestamp,
|
||||||
|
updated_at: timestamp,
|
||||||
|
}] };
|
||||||
|
}
|
||||||
|
if (/from device_routes dr/.test(sql)) {
|
||||||
|
return { rows: [{
|
||||||
|
id: "cccccccc-cccc-4ccc-8ccc-cccccccccccc",
|
||||||
|
route_key: "route-one",
|
||||||
|
display_name: "Route one",
|
||||||
|
edge_id: "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb",
|
||||||
|
edge_name: "Edge one",
|
||||||
|
model_profile_ref: "vendor.model.v1",
|
||||||
|
profile_vendor: "Vendor",
|
||||||
|
profile_model: "Model",
|
||||||
|
listener_ref: "listener:generic",
|
||||||
|
protocol: "INTERNAL",
|
||||||
|
direction: "bidirectional",
|
||||||
|
lifecycle_state: "active",
|
||||||
|
session_count: "1",
|
||||||
|
active_session_count: "1",
|
||||||
|
created_at: timestamp,
|
||||||
|
updated_at: timestamp,
|
||||||
|
}] };
|
||||||
|
}
|
||||||
|
if (/select ds\.id, ds\.route_id, dr\.display_name/.test(sql)) {
|
||||||
|
return { rows: [{
|
||||||
|
id: "dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
||||||
|
route_id: "cccccccc-cccc-4ccc-8ccc-cccccccccccc",
|
||||||
|
route_name: "Route one",
|
||||||
|
device_id: "44444444-4444-4444-8444-444444444444",
|
||||||
|
device_name: "Pilot device",
|
||||||
|
protocol: "INTERNAL",
|
||||||
|
lifecycle_state: "online",
|
||||||
|
connected_at: timestamp,
|
||||||
|
last_seen_at: timestamp,
|
||||||
|
disconnected_at: null,
|
||||||
|
close_reason_code: null,
|
||||||
|
frame_count: "12",
|
||||||
|
byte_count: "1024",
|
||||||
|
}] };
|
||||||
|
}
|
||||||
|
if (/from device_resource_bindings drb/.test(sql)) {
|
||||||
|
return { rows: [{
|
||||||
|
id: "eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee",
|
||||||
|
binding_key: "foundry-map",
|
||||||
|
display_name: "Foundry map",
|
||||||
|
source_kind: "collection",
|
||||||
|
device_id: null,
|
||||||
|
collection_id: "55555555-5555-4555-8555-555555555555",
|
||||||
|
source_name: "Pilot fleet",
|
||||||
|
target_kind: "foundry.application",
|
||||||
|
target_ref: "application:pilot-map",
|
||||||
|
capabilities: ["observe"],
|
||||||
|
lifecycle_state: "pending_external_approval",
|
||||||
|
source_approved_at: timestamp,
|
||||||
|
external_approval_ref: "external-approval-proof",
|
||||||
|
created_at: timestamp,
|
||||||
|
updated_at: timestamp,
|
||||||
|
}] };
|
||||||
|
}
|
||||||
|
if (/from device_configuration_revisions dcr/.test(sql)) {
|
||||||
|
return { rows: [{
|
||||||
|
id: "ffffffff-ffff-4fff-8fff-ffffffffffff",
|
||||||
|
device_id: "44444444-4444-4444-8444-444444444444",
|
||||||
|
device_name: "Pilot device",
|
||||||
|
revision_number: "1",
|
||||||
|
model_profile_ref: "vendor.model.v1",
|
||||||
|
schema_artifact_ref: "schema:vendor.model.v1",
|
||||||
|
configuration_digest: `sha256:${"c".repeat(64)}`,
|
||||||
|
configuration: { raw: "must-not-leak" },
|
||||||
|
change_summary: "Pilot configuration",
|
||||||
|
created_at: timestamp,
|
||||||
|
}] };
|
||||||
|
}
|
||||||
|
if (/from device_configuration_state dcs/.test(sql)) {
|
||||||
|
return { rows: [{
|
||||||
|
device_id: "44444444-4444-4444-8444-444444444444",
|
||||||
|
device_name: "Pilot device",
|
||||||
|
desired_revision_id: "ffffffff-ffff-4fff-8fff-ffffffffffff",
|
||||||
|
applied_revision_id: null,
|
||||||
|
applied_at: null,
|
||||||
|
updated_at: timestamp,
|
||||||
|
}] };
|
||||||
|
}
|
||||||
|
if (/from device_commands dc/.test(sql)) {
|
||||||
|
return { rows: [{
|
||||||
|
id: "12121212-1212-4121-8121-121212121212",
|
||||||
|
device_id: "44444444-4444-4444-8444-444444444444",
|
||||||
|
device_name: "Pilot device",
|
||||||
|
command_key: "safe-ping",
|
||||||
|
command_catalog_ref: "catalog:safe-ping:v1",
|
||||||
|
command_type: "device.ping",
|
||||||
|
risk_class: "low",
|
||||||
|
lifecycle_state: "acknowledged",
|
||||||
|
planned_at: timestamp,
|
||||||
|
expires_at: "2026-08-11T00:00:00.000Z",
|
||||||
|
confirmed_at: timestamp,
|
||||||
|
dispatched_at: timestamp,
|
||||||
|
acknowledged_at: timestamp,
|
||||||
|
terminal_at: null,
|
||||||
|
terminal_reason_code: null,
|
||||||
|
transport_message_ref: "transport-message-secret",
|
||||||
|
parameters_projection: { raw: "must-not-leak" },
|
||||||
|
created_at: timestamp,
|
||||||
|
updated_at: timestamp,
|
||||||
|
}] };
|
||||||
|
}
|
||||||
|
if (/from device_audit_events dae/.test(sql)) {
|
||||||
|
return { rows: [{
|
||||||
|
id: "13131313-1313-4131-8131-131313131313",
|
||||||
|
event_type: "device.observed",
|
||||||
|
actor_ref: "user:device-admin",
|
||||||
|
device_id: "44444444-4444-4444-8444-444444444444",
|
||||||
|
discovery_id: "66666666-6666-4666-8666-666666666666",
|
||||||
|
occurred_at: timestamp,
|
||||||
|
payload: { raw: "raw-audit-payload" },
|
||||||
|
}] };
|
||||||
|
}
|
||||||
|
if (/from device_instances di\n left join lateral/.test(sql)) {
|
||||||
return { rows: [{
|
return { rows: [{
|
||||||
id: "44444444-4444-4444-8444-444444444444",
|
id: "44444444-4444-4444-8444-444444444444",
|
||||||
device_key: "pilot-device",
|
device_key: "pilot-device",
|
||||||
|
|||||||
Reference in New Issue
Block a user