feat(scim): deepen SCIM provisioning — stats, delete, pause/resume
- repository.ts: add ScimConnectorStats interface, getConnectorStats (user/group/event counts), deleteConnector - routes.ts: 4 new endpoints — GET /scim/connectors/:orgId/:id/stats, DELETE /scim/connectors/:orgId/:id (must be paused first), POST /scim/connectors/:orgId/:id/pause, POST /scim/connectors/:orgId/:id/resume - Existing 4 tests unchanged, typecheck clean
This commit is contained in:
parent
20663d7078
commit
d073122a48
@ -91,3 +91,39 @@ export async function listEvents(connectorId: string): Promise<ScimProvisioningE
|
|||||||
limit: 500,
|
limit: 500,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ScimConnectorStats {
|
||||||
|
connectorId: string;
|
||||||
|
userCount: number;
|
||||||
|
groupCount: number;
|
||||||
|
eventCount: number;
|
||||||
|
provisionedUsers: number;
|
||||||
|
failedUsers: number;
|
||||||
|
provisionedGroups: number;
|
||||||
|
failedGroups: number;
|
||||||
|
lastEventAt: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getConnectorStats(connectorId: string): Promise<ScimConnectorStats> {
|
||||||
|
const [users, groups, events] = await Promise.all([
|
||||||
|
listUserSync(connectorId),
|
||||||
|
listGroupSync(connectorId),
|
||||||
|
listEvents(connectorId),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
connectorId,
|
||||||
|
userCount: users.length,
|
||||||
|
groupCount: groups.length,
|
||||||
|
eventCount: events.length,
|
||||||
|
provisionedUsers: users.filter(u => u.status === 'provisioned').length,
|
||||||
|
failedUsers: users.filter(u => u.status === 'failed').length,
|
||||||
|
provisionedGroups: groups.filter(g => g.status === 'provisioned').length,
|
||||||
|
failedGroups: groups.filter(g => g.status === 'failed').length,
|
||||||
|
lastEventAt: events[0]?.createdAt ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteConnector(id: string, orgId: string): Promise<void> {
|
||||||
|
await connectorCollection().delete(id, orgId);
|
||||||
|
}
|
||||||
|
|||||||
@ -175,4 +175,46 @@ export async function scimRoutes(app: FastifyInstance) {
|
|||||||
};
|
};
|
||||||
return repo.createEvent(doc);
|
return repo.createEvent(doc);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── Connector stats ────────────────────────────────────
|
||||||
|
app.get('/scim/connectors/:orgId/:id/stats', async req => {
|
||||||
|
requireAdmin(req);
|
||||||
|
const { orgId, id } = req.params as { orgId: string; id: string };
|
||||||
|
await repo.getConnector(id, orgId);
|
||||||
|
return repo.getConnectorStats(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Delete connector (paused only) ─────────────────────
|
||||||
|
app.delete('/scim/connectors/:orgId/:id', async req => {
|
||||||
|
requireAdmin(req);
|
||||||
|
const { orgId, id } = req.params as { orgId: string; id: string };
|
||||||
|
const connector = await repo.getConnector(id, orgId);
|
||||||
|
if (connector.status === 'active') {
|
||||||
|
throw new BadRequestError('Pause the connector before deleting it');
|
||||||
|
}
|
||||||
|
await repo.deleteConnector(id, orgId);
|
||||||
|
return { deleted: true };
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Pause connector ────────────────────────────────────
|
||||||
|
app.post('/scim/connectors/:orgId/:id/pause', async req => {
|
||||||
|
requireAdmin(req);
|
||||||
|
const { orgId, id } = req.params as { orgId: string; id: string };
|
||||||
|
const connector = await repo.getConnector(id, orgId);
|
||||||
|
if (connector.status !== 'active') {
|
||||||
|
throw new BadRequestError(`Cannot pause connector with status '${connector.status}'`);
|
||||||
|
}
|
||||||
|
return repo.updateConnector(id, orgId, { status: 'paused' });
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Resume connector ───────────────────────────────────
|
||||||
|
app.post('/scim/connectors/:orgId/:id/resume', async req => {
|
||||||
|
requireAdmin(req);
|
||||||
|
const { orgId, id } = req.params as { orgId: string; id: string };
|
||||||
|
const connector = await repo.getConnector(id, orgId);
|
||||||
|
if (connector.status !== 'paused') {
|
||||||
|
throw new BadRequestError(`Cannot resume connector with status '${connector.status}'`);
|
||||||
|
}
|
||||||
|
return repo.updateConnector(id, orgId, { status: 'active' });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user