@openhi/constructs 0.0.99 → 0.0.101
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.
|
@@ -5703,55 +5703,6 @@ function getDefaultPostgresQueryRunner() {
|
|
|
5703
5703
|
return cached;
|
|
5704
5704
|
}
|
|
5705
5705
|
|
|
5706
|
-
// src/data/operations/data/appointment/appointment-search-by-date-operation.ts
|
|
5707
|
-
var DEFAULT_LIMIT = 100;
|
|
5708
|
-
function buildSearchAppointmentsByDateSql(opts) {
|
|
5709
|
-
const datePredicates = buildAppointmentDateSearchPredicateSql(
|
|
5710
|
-
opts.dateConstraints
|
|
5711
|
-
);
|
|
5712
|
-
const lines = [
|
|
5713
|
-
"SELECT resource_id AS id, resource",
|
|
5714
|
-
"FROM resources",
|
|
5715
|
-
"WHERE tenant_id = :tenantId",
|
|
5716
|
-
" AND workspace_id = :workspaceId",
|
|
5717
|
-
" AND resource_type = 'Appointment'",
|
|
5718
|
-
" AND deleted_at IS NULL"
|
|
5719
|
-
];
|
|
5720
|
-
for (const fragment of datePredicates) {
|
|
5721
|
-
lines.push(` AND ${fragment}`);
|
|
5722
|
-
}
|
|
5723
|
-
lines.push("ORDER BY last_updated DESC");
|
|
5724
|
-
lines.push("LIMIT :limit;");
|
|
5725
|
-
return lines.join("\n");
|
|
5726
|
-
}
|
|
5727
|
-
async function searchAppointmentsByDateOperation(params) {
|
|
5728
|
-
const { context, dateConstraints } = params;
|
|
5729
|
-
if (dateConstraints.length === 0) {
|
|
5730
|
-
throw new Error(
|
|
5731
|
-
"searchAppointmentsByDateOperation requires at least one dateConstraint"
|
|
5732
|
-
);
|
|
5733
|
-
}
|
|
5734
|
-
const { tenantId, workspaceId } = context;
|
|
5735
|
-
const runner = params.runner ?? getDefaultPostgresQueryRunner();
|
|
5736
|
-
const limit = params.limit ?? DEFAULT_LIMIT;
|
|
5737
|
-
const sql = buildSearchAppointmentsByDateSql({ dateConstraints });
|
|
5738
|
-
const queryParams = [
|
|
5739
|
-
{ name: "tenantId", value: tenantId },
|
|
5740
|
-
{ name: "workspaceId", value: workspaceId },
|
|
5741
|
-
{ name: "limit", value: limit },
|
|
5742
|
-
...buildAppointmentDateSearchPredicateParams(dateConstraints)
|
|
5743
|
-
];
|
|
5744
|
-
const rows = await runner.query(sql, queryParams);
|
|
5745
|
-
const entries = rows.map((row) => ({
|
|
5746
|
-
id: row.id,
|
|
5747
|
-
resource: {
|
|
5748
|
-
...row.resource,
|
|
5749
|
-
id: row.id
|
|
5750
|
-
}
|
|
5751
|
-
}));
|
|
5752
|
-
return { entries, total: entries.length };
|
|
5753
|
-
}
|
|
5754
|
-
|
|
5755
5706
|
// src/data/operations/data/encounter/encounter-period-search-predicate.ts
|
|
5756
5707
|
var PERIOD_SEARCH_PREFIXES = [
|
|
5757
5708
|
"gt",
|
|
@@ -5804,7 +5755,7 @@ function buildPeriodSearchPredicateParams(constraints) {
|
|
|
5804
5755
|
}
|
|
5805
5756
|
|
|
5806
5757
|
// src/data/operations/data/encounter/encounter-search-by-patient-operation.ts
|
|
5807
|
-
var
|
|
5758
|
+
var DEFAULT_LIMIT = 100;
|
|
5808
5759
|
function buildOpenHiResourceUrn(opts) {
|
|
5809
5760
|
return `urn:ohi:${opts.tenantId}:${opts.workspaceId}:${opts.resourceType}:${opts.resourceId}`;
|
|
5810
5761
|
}
|
|
@@ -5834,7 +5785,7 @@ async function searchEncountersByPatientOperation(params) {
|
|
|
5834
5785
|
const periodConstraints = params.periodConstraints ?? [];
|
|
5835
5786
|
const { tenantId, workspaceId } = context;
|
|
5836
5787
|
const runner = params.runner ?? getDefaultPostgresQueryRunner();
|
|
5837
|
-
const limit = params.limit ??
|
|
5788
|
+
const limit = params.limit ?? DEFAULT_LIMIT;
|
|
5838
5789
|
const containmentRelative = JSON.stringify({
|
|
5839
5790
|
subject: { reference: `Patient/${patientId}` }
|
|
5840
5791
|
});
|
|
@@ -5868,8 +5819,155 @@ async function searchEncountersByPatientOperation(params) {
|
|
|
5868
5819
|
return { entries, total: entries.length };
|
|
5869
5820
|
}
|
|
5870
5821
|
|
|
5871
|
-
// src/data/operations/data/
|
|
5822
|
+
// src/data/operations/data/reference-containment-predicate.ts
|
|
5823
|
+
var REFERENCE_CONTAINMENT_SQL_FRAGMENT = "(resource @> :containmentRelative::jsonb OR resource @> :containmentUrn::jsonb)";
|
|
5824
|
+
function parseTypedReference(s) {
|
|
5825
|
+
const match = /^([A-Za-z][A-Za-z0-9_]*)\/([^\s/]+)$/.exec(s);
|
|
5826
|
+
if (!match) {
|
|
5827
|
+
return void 0;
|
|
5828
|
+
}
|
|
5829
|
+
return { resourceType: match[1], resourceId: match[2] };
|
|
5830
|
+
}
|
|
5831
|
+
function wrapReferenceInShape(shape, reference) {
|
|
5832
|
+
switch (shape.kind) {
|
|
5833
|
+
case "scalar":
|
|
5834
|
+
return { [shape.field]: { reference } };
|
|
5835
|
+
case "array-of-references":
|
|
5836
|
+
return { [shape.field]: [{ reference }] };
|
|
5837
|
+
case "array-of-objects":
|
|
5838
|
+
return { [shape.field]: [{ [shape.subfield]: { reference } }] };
|
|
5839
|
+
}
|
|
5840
|
+
}
|
|
5841
|
+
function buildReferenceContainmentPayload(params) {
|
|
5842
|
+
const parsed = parseTypedReference(params.reference);
|
|
5843
|
+
if (!parsed) {
|
|
5844
|
+
throw new Error(
|
|
5845
|
+
`Reference "${params.reference}" is not a valid typed reference (<ResourceType>/<id>).`
|
|
5846
|
+
);
|
|
5847
|
+
}
|
|
5848
|
+
const urn = buildOpenHiResourceUrn({
|
|
5849
|
+
tenantId: params.tenantId,
|
|
5850
|
+
workspaceId: params.workspaceId,
|
|
5851
|
+
resourceType: parsed.resourceType,
|
|
5852
|
+
resourceId: parsed.resourceId
|
|
5853
|
+
});
|
|
5854
|
+
return {
|
|
5855
|
+
containmentRelative: JSON.stringify(
|
|
5856
|
+
wrapReferenceInShape(params.shape, params.reference)
|
|
5857
|
+
),
|
|
5858
|
+
containmentUrn: JSON.stringify(wrapReferenceInShape(params.shape, urn))
|
|
5859
|
+
};
|
|
5860
|
+
}
|
|
5861
|
+
|
|
5862
|
+
// src/data/operations/data/appointment/appointment-search-by-actor-operation.ts
|
|
5863
|
+
var DEFAULT_LIMIT2 = 100;
|
|
5864
|
+
function buildSearchAppointmentsByActorSql(opts) {
|
|
5865
|
+
const datePredicates = buildAppointmentDateSearchPredicateSql(
|
|
5866
|
+
opts?.dateConstraints ?? []
|
|
5867
|
+
);
|
|
5868
|
+
const lines = [
|
|
5869
|
+
"SELECT resource_id AS id, resource",
|
|
5870
|
+
"FROM resources",
|
|
5871
|
+
"WHERE tenant_id = :tenantId",
|
|
5872
|
+
" AND workspace_id = :workspaceId",
|
|
5873
|
+
" AND resource_type = 'Appointment'",
|
|
5874
|
+
" AND deleted_at IS NULL",
|
|
5875
|
+
` AND ${REFERENCE_CONTAINMENT_SQL_FRAGMENT}`
|
|
5876
|
+
];
|
|
5877
|
+
for (const fragment of datePredicates) {
|
|
5878
|
+
lines.push(` AND ${fragment}`);
|
|
5879
|
+
}
|
|
5880
|
+
lines.push("ORDER BY last_updated DESC");
|
|
5881
|
+
lines.push("LIMIT :limit;");
|
|
5882
|
+
return lines.join("\n");
|
|
5883
|
+
}
|
|
5884
|
+
async function searchAppointmentsByActorOperation(params) {
|
|
5885
|
+
const { context, actorReference } = params;
|
|
5886
|
+
const dateConstraints = params.dateConstraints ?? [];
|
|
5887
|
+
const { tenantId, workspaceId } = context;
|
|
5888
|
+
const runner = params.runner ?? getDefaultPostgresQueryRunner();
|
|
5889
|
+
const limit = params.limit ?? DEFAULT_LIMIT2;
|
|
5890
|
+
const { containmentRelative, containmentUrn } = buildReferenceContainmentPayload({
|
|
5891
|
+
shape: {
|
|
5892
|
+
kind: "array-of-objects",
|
|
5893
|
+
field: "participant",
|
|
5894
|
+
subfield: "actor"
|
|
5895
|
+
},
|
|
5896
|
+
reference: actorReference,
|
|
5897
|
+
tenantId,
|
|
5898
|
+
workspaceId
|
|
5899
|
+
});
|
|
5900
|
+
const sql = buildSearchAppointmentsByActorSql({ dateConstraints });
|
|
5901
|
+
const queryParams = [
|
|
5902
|
+
{ name: "tenantId", value: tenantId },
|
|
5903
|
+
{ name: "workspaceId", value: workspaceId },
|
|
5904
|
+
{ name: "containmentRelative", value: containmentRelative },
|
|
5905
|
+
{ name: "containmentUrn", value: containmentUrn },
|
|
5906
|
+
{ name: "limit", value: limit },
|
|
5907
|
+
...buildAppointmentDateSearchPredicateParams(dateConstraints)
|
|
5908
|
+
];
|
|
5909
|
+
const rows = await runner.query(sql, queryParams);
|
|
5910
|
+
const entries = rows.map((row) => ({
|
|
5911
|
+
id: row.id,
|
|
5912
|
+
resource: {
|
|
5913
|
+
...row.resource,
|
|
5914
|
+
id: row.id
|
|
5915
|
+
}
|
|
5916
|
+
}));
|
|
5917
|
+
return { entries, total: entries.length };
|
|
5918
|
+
}
|
|
5919
|
+
|
|
5920
|
+
// src/data/operations/data/appointment/appointment-search-by-date-operation.ts
|
|
5872
5921
|
var DEFAULT_LIMIT3 = 100;
|
|
5922
|
+
function buildSearchAppointmentsByDateSql(opts) {
|
|
5923
|
+
const datePredicates = buildAppointmentDateSearchPredicateSql(
|
|
5924
|
+
opts.dateConstraints
|
|
5925
|
+
);
|
|
5926
|
+
const lines = [
|
|
5927
|
+
"SELECT resource_id AS id, resource",
|
|
5928
|
+
"FROM resources",
|
|
5929
|
+
"WHERE tenant_id = :tenantId",
|
|
5930
|
+
" AND workspace_id = :workspaceId",
|
|
5931
|
+
" AND resource_type = 'Appointment'",
|
|
5932
|
+
" AND deleted_at IS NULL"
|
|
5933
|
+
];
|
|
5934
|
+
for (const fragment of datePredicates) {
|
|
5935
|
+
lines.push(` AND ${fragment}`);
|
|
5936
|
+
}
|
|
5937
|
+
lines.push("ORDER BY last_updated DESC");
|
|
5938
|
+
lines.push("LIMIT :limit;");
|
|
5939
|
+
return lines.join("\n");
|
|
5940
|
+
}
|
|
5941
|
+
async function searchAppointmentsByDateOperation(params) {
|
|
5942
|
+
const { context, dateConstraints } = params;
|
|
5943
|
+
if (dateConstraints.length === 0) {
|
|
5944
|
+
throw new Error(
|
|
5945
|
+
"searchAppointmentsByDateOperation requires at least one dateConstraint"
|
|
5946
|
+
);
|
|
5947
|
+
}
|
|
5948
|
+
const { tenantId, workspaceId } = context;
|
|
5949
|
+
const runner = params.runner ?? getDefaultPostgresQueryRunner();
|
|
5950
|
+
const limit = params.limit ?? DEFAULT_LIMIT3;
|
|
5951
|
+
const sql = buildSearchAppointmentsByDateSql({ dateConstraints });
|
|
5952
|
+
const queryParams = [
|
|
5953
|
+
{ name: "tenantId", value: tenantId },
|
|
5954
|
+
{ name: "workspaceId", value: workspaceId },
|
|
5955
|
+
{ name: "limit", value: limit },
|
|
5956
|
+
...buildAppointmentDateSearchPredicateParams(dateConstraints)
|
|
5957
|
+
];
|
|
5958
|
+
const rows = await runner.query(sql, queryParams);
|
|
5959
|
+
const entries = rows.map((row) => ({
|
|
5960
|
+
id: row.id,
|
|
5961
|
+
resource: {
|
|
5962
|
+
...row.resource,
|
|
5963
|
+
id: row.id
|
|
5964
|
+
}
|
|
5965
|
+
}));
|
|
5966
|
+
return { entries, total: entries.length };
|
|
5967
|
+
}
|
|
5968
|
+
|
|
5969
|
+
// src/data/operations/data/appointment/appointment-search-by-patient-operation.ts
|
|
5970
|
+
var DEFAULT_LIMIT4 = 100;
|
|
5873
5971
|
function buildSearchAppointmentsByPatientSql(opts) {
|
|
5874
5972
|
const datePredicates = buildAppointmentDateSearchPredicateSql(
|
|
5875
5973
|
opts?.dateConstraints ?? []
|
|
@@ -5896,7 +5994,7 @@ async function searchAppointmentsByPatientOperation(params) {
|
|
|
5896
5994
|
const dateConstraints = params.dateConstraints ?? [];
|
|
5897
5995
|
const { tenantId, workspaceId } = context;
|
|
5898
5996
|
const runner = params.runner ?? getDefaultPostgresQueryRunner();
|
|
5899
|
-
const limit = params.limit ??
|
|
5997
|
+
const limit = params.limit ?? DEFAULT_LIMIT4;
|
|
5900
5998
|
const containmentRelative = JSON.stringify({
|
|
5901
5999
|
participant: [{ actor: { reference: `Patient/${patientId}` } }]
|
|
5902
6000
|
});
|
|
@@ -5983,11 +6081,45 @@ function sendInvalidSearch400(res, diagnostics) {
|
|
|
5983
6081
|
}
|
|
5984
6082
|
async function listAppointmentsRoute(req, res) {
|
|
5985
6083
|
const patientId = singleStringQueryParam(req, "patient");
|
|
6084
|
+
const actorRef = singleStringQueryParam(req, "actor");
|
|
5986
6085
|
const parsed = parseAppointmentDateConstraints(req);
|
|
5987
6086
|
if (isError(parsed)) {
|
|
5988
6087
|
return sendInvalidSearch400(res, parsed.error);
|
|
5989
6088
|
}
|
|
5990
6089
|
const dateConstraints = parsed;
|
|
6090
|
+
if (patientId !== void 0 && actorRef !== void 0) {
|
|
6091
|
+
return sendInvalidSearch400(
|
|
6092
|
+
res,
|
|
6093
|
+
"?patient= and ?actor= cannot be combined on the same request."
|
|
6094
|
+
);
|
|
6095
|
+
}
|
|
6096
|
+
if (actorRef !== void 0) {
|
|
6097
|
+
if (parseTypedReference(actorRef) === void 0) {
|
|
6098
|
+
return sendInvalidSearch400(
|
|
6099
|
+
res,
|
|
6100
|
+
`?actor must be a typed reference like "Practitioner/<id>"; got "${actorRef}".`
|
|
6101
|
+
);
|
|
6102
|
+
}
|
|
6103
|
+
const ctx = req.openhiContext;
|
|
6104
|
+
try {
|
|
6105
|
+
const result = await searchAppointmentsByActorOperation({
|
|
6106
|
+
context: ctx,
|
|
6107
|
+
actorReference: actorRef,
|
|
6108
|
+
dateConstraints
|
|
6109
|
+
});
|
|
6110
|
+
const bundle = buildSearchsetBundle(
|
|
6111
|
+
BASE_PATH.APPOINTMENT,
|
|
6112
|
+
result.entries
|
|
6113
|
+
);
|
|
6114
|
+
return res.json(bundle);
|
|
6115
|
+
} catch (err) {
|
|
6116
|
+
return sendOperationOutcome500(
|
|
6117
|
+
res,
|
|
6118
|
+
err,
|
|
6119
|
+
"GET /Appointment?actor= search error:"
|
|
6120
|
+
);
|
|
6121
|
+
}
|
|
6122
|
+
}
|
|
5991
6123
|
if (patientId !== void 0) {
|
|
5992
6124
|
const ctx = req.openhiContext;
|
|
5993
6125
|
try {
|
|
@@ -13579,7 +13711,7 @@ async function listEncountersOperation(params) {
|
|
|
13579
13711
|
}
|
|
13580
13712
|
|
|
13581
13713
|
// src/data/operations/data/encounter/encounter-search-by-date-operation.ts
|
|
13582
|
-
var
|
|
13714
|
+
var DEFAULT_LIMIT5 = 100;
|
|
13583
13715
|
function buildSearchEncountersByDateSql(opts) {
|
|
13584
13716
|
const periodPredicates = buildPeriodSearchPredicateSql(
|
|
13585
13717
|
opts.periodConstraints
|
|
@@ -13608,7 +13740,7 @@ async function searchEncountersByDateOperation(params) {
|
|
|
13608
13740
|
}
|
|
13609
13741
|
const { tenantId, workspaceId } = context;
|
|
13610
13742
|
const runner = params.runner ?? getDefaultPostgresQueryRunner();
|
|
13611
|
-
const limit = params.limit ??
|
|
13743
|
+
const limit = params.limit ?? DEFAULT_LIMIT5;
|
|
13612
13744
|
const sql = buildSearchEncountersByDateSql({ periodConstraints });
|
|
13613
13745
|
const queryParams = [
|
|
13614
13746
|
{ name: "tenantId", value: tenantId },
|
|
@@ -13627,6 +13759,64 @@ async function searchEncountersByDateOperation(params) {
|
|
|
13627
13759
|
return { entries, total: entries.length };
|
|
13628
13760
|
}
|
|
13629
13761
|
|
|
13762
|
+
// src/data/operations/data/encounter/encounter-search-by-participant-operation.ts
|
|
13763
|
+
var DEFAULT_LIMIT6 = 100;
|
|
13764
|
+
function buildSearchEncountersByParticipantSql(opts) {
|
|
13765
|
+
const periodPredicates = buildPeriodSearchPredicateSql(
|
|
13766
|
+
opts?.periodConstraints ?? []
|
|
13767
|
+
);
|
|
13768
|
+
const lines = [
|
|
13769
|
+
"SELECT resource_id AS id, resource",
|
|
13770
|
+
"FROM resources",
|
|
13771
|
+
"WHERE tenant_id = :tenantId",
|
|
13772
|
+
" AND workspace_id = :workspaceId",
|
|
13773
|
+
" AND resource_type = 'Encounter'",
|
|
13774
|
+
" AND deleted_at IS NULL",
|
|
13775
|
+
` AND ${REFERENCE_CONTAINMENT_SQL_FRAGMENT}`
|
|
13776
|
+
];
|
|
13777
|
+
for (const fragment of periodPredicates) {
|
|
13778
|
+
lines.push(` AND ${fragment}`);
|
|
13779
|
+
}
|
|
13780
|
+
lines.push("ORDER BY last_updated DESC");
|
|
13781
|
+
lines.push("LIMIT :limit;");
|
|
13782
|
+
return lines.join("\n");
|
|
13783
|
+
}
|
|
13784
|
+
async function searchEncountersByParticipantOperation(params) {
|
|
13785
|
+
const { context, participantReference } = params;
|
|
13786
|
+
const periodConstraints = params.periodConstraints ?? [];
|
|
13787
|
+
const { tenantId, workspaceId } = context;
|
|
13788
|
+
const runner = params.runner ?? getDefaultPostgresQueryRunner();
|
|
13789
|
+
const limit = params.limit ?? DEFAULT_LIMIT6;
|
|
13790
|
+
const { containmentRelative, containmentUrn } = buildReferenceContainmentPayload({
|
|
13791
|
+
shape: {
|
|
13792
|
+
kind: "array-of-objects",
|
|
13793
|
+
field: "participant",
|
|
13794
|
+
subfield: "individual"
|
|
13795
|
+
},
|
|
13796
|
+
reference: participantReference,
|
|
13797
|
+
tenantId,
|
|
13798
|
+
workspaceId
|
|
13799
|
+
});
|
|
13800
|
+
const sql = buildSearchEncountersByParticipantSql({ periodConstraints });
|
|
13801
|
+
const queryParams = [
|
|
13802
|
+
{ name: "tenantId", value: tenantId },
|
|
13803
|
+
{ name: "workspaceId", value: workspaceId },
|
|
13804
|
+
{ name: "containmentRelative", value: containmentRelative },
|
|
13805
|
+
{ name: "containmentUrn", value: containmentUrn },
|
|
13806
|
+
{ name: "limit", value: limit },
|
|
13807
|
+
...buildPeriodSearchPredicateParams(periodConstraints)
|
|
13808
|
+
];
|
|
13809
|
+
const rows = await runner.query(sql, queryParams);
|
|
13810
|
+
const entries = rows.map((row) => ({
|
|
13811
|
+
id: row.id,
|
|
13812
|
+
resource: {
|
|
13813
|
+
...row.resource,
|
|
13814
|
+
id: row.id
|
|
13815
|
+
}
|
|
13816
|
+
}));
|
|
13817
|
+
return { entries, total: entries.length };
|
|
13818
|
+
}
|
|
13819
|
+
|
|
13630
13820
|
// src/data/rest-api/routes/data/encounter/encounter-list-route.ts
|
|
13631
13821
|
function singleStringQueryParam2(req, name) {
|
|
13632
13822
|
const v = req.query[name];
|
|
@@ -13676,11 +13866,42 @@ function sendInvalidSearch4002(res, diagnostics) {
|
|
|
13676
13866
|
}
|
|
13677
13867
|
async function listEncountersRoute(req, res) {
|
|
13678
13868
|
const patientId = singleStringQueryParam2(req, "patient");
|
|
13869
|
+
const participantRef = singleStringQueryParam2(req, "participant");
|
|
13679
13870
|
const parsed = parseEncounterDateConstraints(req);
|
|
13680
13871
|
if (isError2(parsed)) {
|
|
13681
13872
|
return sendInvalidSearch4002(res, parsed.error);
|
|
13682
13873
|
}
|
|
13683
13874
|
const periodConstraints = parsed;
|
|
13875
|
+
if (patientId !== void 0 && participantRef !== void 0) {
|
|
13876
|
+
return sendInvalidSearch4002(
|
|
13877
|
+
res,
|
|
13878
|
+
"?patient= and ?participant= cannot be combined on the same request."
|
|
13879
|
+
);
|
|
13880
|
+
}
|
|
13881
|
+
if (participantRef !== void 0) {
|
|
13882
|
+
if (parseTypedReference(participantRef) === void 0) {
|
|
13883
|
+
return sendInvalidSearch4002(
|
|
13884
|
+
res,
|
|
13885
|
+
`?participant must be a typed reference like "Practitioner/<id>"; got "${participantRef}".`
|
|
13886
|
+
);
|
|
13887
|
+
}
|
|
13888
|
+
const ctx = req.openhiContext;
|
|
13889
|
+
try {
|
|
13890
|
+
const result = await searchEncountersByParticipantOperation({
|
|
13891
|
+
context: ctx,
|
|
13892
|
+
participantReference: participantRef,
|
|
13893
|
+
periodConstraints
|
|
13894
|
+
});
|
|
13895
|
+
const bundle = buildSearchsetBundle(BASE_PATH.ENCOUNTER, result.entries);
|
|
13896
|
+
return res.json(bundle);
|
|
13897
|
+
} catch (err) {
|
|
13898
|
+
return sendOperationOutcome500(
|
|
13899
|
+
res,
|
|
13900
|
+
err,
|
|
13901
|
+
"GET /Encounter?participant= search error:"
|
|
13902
|
+
);
|
|
13903
|
+
}
|
|
13904
|
+
}
|
|
13684
13905
|
if (patientId !== void 0) {
|
|
13685
13906
|
const ctx = req.openhiContext;
|
|
13686
13907
|
try {
|
|
@@ -25481,8 +25702,91 @@ async function listPatientsOperation(params) {
|
|
|
25481
25702
|
);
|
|
25482
25703
|
}
|
|
25483
25704
|
|
|
25705
|
+
// src/data/operations/data/patient/patient-search-by-general-practitioner-operation.ts
|
|
25706
|
+
var DEFAULT_LIMIT7 = 100;
|
|
25707
|
+
function buildSearchPatientsByGeneralPractitionerSql() {
|
|
25708
|
+
return [
|
|
25709
|
+
"SELECT resource_id AS id, resource",
|
|
25710
|
+
"FROM resources",
|
|
25711
|
+
"WHERE tenant_id = :tenantId",
|
|
25712
|
+
" AND workspace_id = :workspaceId",
|
|
25713
|
+
" AND resource_type = 'Patient'",
|
|
25714
|
+
" AND deleted_at IS NULL",
|
|
25715
|
+
` AND ${REFERENCE_CONTAINMENT_SQL_FRAGMENT}`,
|
|
25716
|
+
"ORDER BY last_updated DESC",
|
|
25717
|
+
"LIMIT :limit;"
|
|
25718
|
+
].join("\n");
|
|
25719
|
+
}
|
|
25720
|
+
async function searchPatientsByGeneralPractitionerOperation(params) {
|
|
25721
|
+
const { context, generalPractitionerReference } = params;
|
|
25722
|
+
const { tenantId, workspaceId } = context;
|
|
25723
|
+
const runner = params.runner ?? getDefaultPostgresQueryRunner();
|
|
25724
|
+
const limit = params.limit ?? DEFAULT_LIMIT7;
|
|
25725
|
+
const { containmentRelative, containmentUrn } = buildReferenceContainmentPayload({
|
|
25726
|
+
shape: { kind: "array-of-references", field: "generalPractitioner" },
|
|
25727
|
+
reference: generalPractitionerReference,
|
|
25728
|
+
tenantId,
|
|
25729
|
+
workspaceId
|
|
25730
|
+
});
|
|
25731
|
+
const sql = buildSearchPatientsByGeneralPractitionerSql();
|
|
25732
|
+
const queryParams = [
|
|
25733
|
+
{ name: "tenantId", value: tenantId },
|
|
25734
|
+
{ name: "workspaceId", value: workspaceId },
|
|
25735
|
+
{ name: "containmentRelative", value: containmentRelative },
|
|
25736
|
+
{ name: "containmentUrn", value: containmentUrn },
|
|
25737
|
+
{ name: "limit", value: limit }
|
|
25738
|
+
];
|
|
25739
|
+
const rows = await runner.query(sql, queryParams);
|
|
25740
|
+
const entries = rows.map((row) => ({
|
|
25741
|
+
id: row.id,
|
|
25742
|
+
resource: { ...row.resource, id: row.id }
|
|
25743
|
+
}));
|
|
25744
|
+
return { entries, total: entries.length };
|
|
25745
|
+
}
|
|
25746
|
+
|
|
25484
25747
|
// src/data/rest-api/routes/data/patient/patient-list-route.ts
|
|
25748
|
+
function singleStringQueryParam3(req, name) {
|
|
25749
|
+
const v = req.query[name];
|
|
25750
|
+
if (typeof v !== "string") {
|
|
25751
|
+
return void 0;
|
|
25752
|
+
}
|
|
25753
|
+
const trimmed = v.trim();
|
|
25754
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
25755
|
+
}
|
|
25756
|
+
function sendInvalidSearch4003(res, diagnostics) {
|
|
25757
|
+
return res.status(400).json({
|
|
25758
|
+
resourceType: "OperationOutcome",
|
|
25759
|
+
issue: [{ severity: "error", code: "invalid", diagnostics }]
|
|
25760
|
+
});
|
|
25761
|
+
}
|
|
25485
25762
|
async function listPatientsRoute(req, res) {
|
|
25763
|
+
const generalPractitionerRef = singleStringQueryParam3(
|
|
25764
|
+
req,
|
|
25765
|
+
"general-practitioner"
|
|
25766
|
+
);
|
|
25767
|
+
if (generalPractitionerRef !== void 0) {
|
|
25768
|
+
if (parseTypedReference(generalPractitionerRef) === void 0) {
|
|
25769
|
+
return sendInvalidSearch4003(
|
|
25770
|
+
res,
|
|
25771
|
+
`?general-practitioner must be a typed reference like "Practitioner/<id>"; got "${generalPractitionerRef}".`
|
|
25772
|
+
);
|
|
25773
|
+
}
|
|
25774
|
+
const ctx = req.openhiContext;
|
|
25775
|
+
try {
|
|
25776
|
+
const result = await searchPatientsByGeneralPractitionerOperation({
|
|
25777
|
+
context: ctx,
|
|
25778
|
+
generalPractitionerReference: generalPractitionerRef
|
|
25779
|
+
});
|
|
25780
|
+
const bundle = buildSearchsetBundle(BASE_PATH.PATIENT, result.entries);
|
|
25781
|
+
return res.json(bundle);
|
|
25782
|
+
} catch (err) {
|
|
25783
|
+
return sendOperationOutcome500(
|
|
25784
|
+
res,
|
|
25785
|
+
err,
|
|
25786
|
+
"GET /Patient?general-practitioner= search error:"
|
|
25787
|
+
);
|
|
25788
|
+
}
|
|
25789
|
+
}
|
|
25486
25790
|
return handleListRoute({
|
|
25487
25791
|
req,
|
|
25488
25792
|
res,
|
|
@@ -29369,8 +29673,90 @@ async function listSchedulesOperation(params) {
|
|
|
29369
29673
|
);
|
|
29370
29674
|
}
|
|
29371
29675
|
|
|
29676
|
+
// src/data/operations/data/schedule/schedule-search-by-actor-operation.ts
|
|
29677
|
+
var DEFAULT_LIMIT8 = 100;
|
|
29678
|
+
function buildSearchSchedulesByActorSql() {
|
|
29679
|
+
return [
|
|
29680
|
+
"SELECT resource_id AS id, resource",
|
|
29681
|
+
"FROM resources",
|
|
29682
|
+
"WHERE tenant_id = :tenantId",
|
|
29683
|
+
" AND workspace_id = :workspaceId",
|
|
29684
|
+
" AND resource_type = 'Schedule'",
|
|
29685
|
+
" AND deleted_at IS NULL",
|
|
29686
|
+
` AND ${REFERENCE_CONTAINMENT_SQL_FRAGMENT}`,
|
|
29687
|
+
"ORDER BY last_updated DESC",
|
|
29688
|
+
"LIMIT :limit;"
|
|
29689
|
+
].join("\n");
|
|
29690
|
+
}
|
|
29691
|
+
async function searchSchedulesByActorOperation(params) {
|
|
29692
|
+
const { context, actorReference } = params;
|
|
29693
|
+
const { tenantId, workspaceId } = context;
|
|
29694
|
+
const runner = params.runner ?? getDefaultPostgresQueryRunner();
|
|
29695
|
+
const limit = params.limit ?? DEFAULT_LIMIT8;
|
|
29696
|
+
const { containmentRelative, containmentUrn } = buildReferenceContainmentPayload({
|
|
29697
|
+
shape: { kind: "array-of-references", field: "actor" },
|
|
29698
|
+
reference: actorReference,
|
|
29699
|
+
tenantId,
|
|
29700
|
+
workspaceId
|
|
29701
|
+
});
|
|
29702
|
+
const queryParams = [
|
|
29703
|
+
{ name: "tenantId", value: tenantId },
|
|
29704
|
+
{ name: "workspaceId", value: workspaceId },
|
|
29705
|
+
{ name: "containmentRelative", value: containmentRelative },
|
|
29706
|
+
{ name: "containmentUrn", value: containmentUrn },
|
|
29707
|
+
{ name: "limit", value: limit }
|
|
29708
|
+
];
|
|
29709
|
+
const rows = await runner.query(
|
|
29710
|
+
buildSearchSchedulesByActorSql(),
|
|
29711
|
+
queryParams
|
|
29712
|
+
);
|
|
29713
|
+
const entries = rows.map((row) => ({
|
|
29714
|
+
id: row.id,
|
|
29715
|
+
resource: { ...row.resource, id: row.id }
|
|
29716
|
+
}));
|
|
29717
|
+
return { entries, total: entries.length };
|
|
29718
|
+
}
|
|
29719
|
+
|
|
29372
29720
|
// src/data/rest-api/routes/data/schedule/schedule-list-route.ts
|
|
29721
|
+
function singleStringQueryParam4(req, name) {
|
|
29722
|
+
const v = req.query[name];
|
|
29723
|
+
if (typeof v !== "string") {
|
|
29724
|
+
return void 0;
|
|
29725
|
+
}
|
|
29726
|
+
const trimmed = v.trim();
|
|
29727
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
29728
|
+
}
|
|
29729
|
+
function sendInvalidSearch4004(res, diagnostics) {
|
|
29730
|
+
return res.status(400).json({
|
|
29731
|
+
resourceType: "OperationOutcome",
|
|
29732
|
+
issue: [{ severity: "error", code: "invalid", diagnostics }]
|
|
29733
|
+
});
|
|
29734
|
+
}
|
|
29373
29735
|
async function listSchedulesRoute(req, res) {
|
|
29736
|
+
const actorRef = singleStringQueryParam4(req, "actor");
|
|
29737
|
+
if (actorRef !== void 0) {
|
|
29738
|
+
if (parseTypedReference(actorRef) === void 0) {
|
|
29739
|
+
return sendInvalidSearch4004(
|
|
29740
|
+
res,
|
|
29741
|
+
`?actor must be a typed reference like "Practitioner/<id>"; got "${actorRef}".`
|
|
29742
|
+
);
|
|
29743
|
+
}
|
|
29744
|
+
const ctx = req.openhiContext;
|
|
29745
|
+
try {
|
|
29746
|
+
const result = await searchSchedulesByActorOperation({
|
|
29747
|
+
context: ctx,
|
|
29748
|
+
actorReference: actorRef
|
|
29749
|
+
});
|
|
29750
|
+
const bundle = buildSearchsetBundle(BASE_PATH.SCHEDULE, result.entries);
|
|
29751
|
+
return res.json(bundle);
|
|
29752
|
+
} catch (err) {
|
|
29753
|
+
return sendOperationOutcome500(
|
|
29754
|
+
res,
|
|
29755
|
+
err,
|
|
29756
|
+
"GET /Schedule?actor= search error:"
|
|
29757
|
+
);
|
|
29758
|
+
}
|
|
29759
|
+
}
|
|
29374
29760
|
return handleListRoute({
|
|
29375
29761
|
req,
|
|
29376
29762
|
res,
|
|
@@ -33051,8 +33437,164 @@ async function listTasksOperation(params) {
|
|
|
33051
33437
|
);
|
|
33052
33438
|
}
|
|
33053
33439
|
|
|
33440
|
+
// src/data/operations/data/task/task-search-by-owner-operation.ts
|
|
33441
|
+
var DEFAULT_LIMIT9 = 100;
|
|
33442
|
+
function buildSearchTasksByOwnerSql() {
|
|
33443
|
+
return [
|
|
33444
|
+
"SELECT resource_id AS id, resource",
|
|
33445
|
+
"FROM resources",
|
|
33446
|
+
"WHERE tenant_id = :tenantId",
|
|
33447
|
+
" AND workspace_id = :workspaceId",
|
|
33448
|
+
" AND resource_type = 'Task'",
|
|
33449
|
+
" AND deleted_at IS NULL",
|
|
33450
|
+
` AND ${REFERENCE_CONTAINMENT_SQL_FRAGMENT}`,
|
|
33451
|
+
"ORDER BY last_updated DESC",
|
|
33452
|
+
"LIMIT :limit;"
|
|
33453
|
+
].join("\n");
|
|
33454
|
+
}
|
|
33455
|
+
async function searchTasksByOwnerOperation(params) {
|
|
33456
|
+
const { context, ownerReference } = params;
|
|
33457
|
+
const { tenantId, workspaceId } = context;
|
|
33458
|
+
const runner = params.runner ?? getDefaultPostgresQueryRunner();
|
|
33459
|
+
const limit = params.limit ?? DEFAULT_LIMIT9;
|
|
33460
|
+
const { containmentRelative, containmentUrn } = buildReferenceContainmentPayload({
|
|
33461
|
+
shape: { kind: "scalar", field: "owner" },
|
|
33462
|
+
reference: ownerReference,
|
|
33463
|
+
tenantId,
|
|
33464
|
+
workspaceId
|
|
33465
|
+
});
|
|
33466
|
+
const queryParams = [
|
|
33467
|
+
{ name: "tenantId", value: tenantId },
|
|
33468
|
+
{ name: "workspaceId", value: workspaceId },
|
|
33469
|
+
{ name: "containmentRelative", value: containmentRelative },
|
|
33470
|
+
{ name: "containmentUrn", value: containmentUrn },
|
|
33471
|
+
{ name: "limit", value: limit }
|
|
33472
|
+
];
|
|
33473
|
+
const rows = await runner.query(
|
|
33474
|
+
buildSearchTasksByOwnerSql(),
|
|
33475
|
+
queryParams
|
|
33476
|
+
);
|
|
33477
|
+
const entries = rows.map((row) => ({
|
|
33478
|
+
id: row.id,
|
|
33479
|
+
resource: { ...row.resource, id: row.id }
|
|
33480
|
+
}));
|
|
33481
|
+
return { entries, total: entries.length };
|
|
33482
|
+
}
|
|
33483
|
+
|
|
33484
|
+
// src/data/operations/data/task/task-search-by-requester-operation.ts
|
|
33485
|
+
var DEFAULT_LIMIT10 = 100;
|
|
33486
|
+
function buildSearchTasksByRequesterSql() {
|
|
33487
|
+
return [
|
|
33488
|
+
"SELECT resource_id AS id, resource",
|
|
33489
|
+
"FROM resources",
|
|
33490
|
+
"WHERE tenant_id = :tenantId",
|
|
33491
|
+
" AND workspace_id = :workspaceId",
|
|
33492
|
+
" AND resource_type = 'Task'",
|
|
33493
|
+
" AND deleted_at IS NULL",
|
|
33494
|
+
` AND ${REFERENCE_CONTAINMENT_SQL_FRAGMENT}`,
|
|
33495
|
+
"ORDER BY last_updated DESC",
|
|
33496
|
+
"LIMIT :limit;"
|
|
33497
|
+
].join("\n");
|
|
33498
|
+
}
|
|
33499
|
+
async function searchTasksByRequesterOperation(params) {
|
|
33500
|
+
const { context, requesterReference } = params;
|
|
33501
|
+
const { tenantId, workspaceId } = context;
|
|
33502
|
+
const runner = params.runner ?? getDefaultPostgresQueryRunner();
|
|
33503
|
+
const limit = params.limit ?? DEFAULT_LIMIT10;
|
|
33504
|
+
const { containmentRelative, containmentUrn } = buildReferenceContainmentPayload({
|
|
33505
|
+
shape: { kind: "scalar", field: "requester" },
|
|
33506
|
+
reference: requesterReference,
|
|
33507
|
+
tenantId,
|
|
33508
|
+
workspaceId
|
|
33509
|
+
});
|
|
33510
|
+
const queryParams = [
|
|
33511
|
+
{ name: "tenantId", value: tenantId },
|
|
33512
|
+
{ name: "workspaceId", value: workspaceId },
|
|
33513
|
+
{ name: "containmentRelative", value: containmentRelative },
|
|
33514
|
+
{ name: "containmentUrn", value: containmentUrn },
|
|
33515
|
+
{ name: "limit", value: limit }
|
|
33516
|
+
];
|
|
33517
|
+
const rows = await runner.query(
|
|
33518
|
+
buildSearchTasksByRequesterSql(),
|
|
33519
|
+
queryParams
|
|
33520
|
+
);
|
|
33521
|
+
const entries = rows.map((row) => ({
|
|
33522
|
+
id: row.id,
|
|
33523
|
+
resource: { ...row.resource, id: row.id }
|
|
33524
|
+
}));
|
|
33525
|
+
return { entries, total: entries.length };
|
|
33526
|
+
}
|
|
33527
|
+
|
|
33054
33528
|
// src/data/rest-api/routes/data/task/task-list-route.ts
|
|
33529
|
+
function singleStringQueryParam5(req, name) {
|
|
33530
|
+
const v = req.query[name];
|
|
33531
|
+
if (typeof v !== "string") {
|
|
33532
|
+
return void 0;
|
|
33533
|
+
}
|
|
33534
|
+
const trimmed = v.trim();
|
|
33535
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
33536
|
+
}
|
|
33537
|
+
function sendInvalidSearch4005(res, diagnostics) {
|
|
33538
|
+
return res.status(400).json({
|
|
33539
|
+
resourceType: "OperationOutcome",
|
|
33540
|
+
issue: [{ severity: "error", code: "invalid", diagnostics }]
|
|
33541
|
+
});
|
|
33542
|
+
}
|
|
33055
33543
|
async function listTasksRoute(req, res) {
|
|
33544
|
+
const ownerRef = singleStringQueryParam5(req, "owner");
|
|
33545
|
+
const requesterRef = singleStringQueryParam5(req, "requester");
|
|
33546
|
+
if (ownerRef !== void 0 && requesterRef !== void 0) {
|
|
33547
|
+
return sendInvalidSearch4005(
|
|
33548
|
+
res,
|
|
33549
|
+
"?owner= and ?requester= cannot be combined on the same request."
|
|
33550
|
+
);
|
|
33551
|
+
}
|
|
33552
|
+
if (ownerRef !== void 0) {
|
|
33553
|
+
if (parseTypedReference(ownerRef) === void 0) {
|
|
33554
|
+
return sendInvalidSearch4005(
|
|
33555
|
+
res,
|
|
33556
|
+
`?owner must be a typed reference like "Practitioner/<id>"; got "${ownerRef}".`
|
|
33557
|
+
);
|
|
33558
|
+
}
|
|
33559
|
+
const ctx = req.openhiContext;
|
|
33560
|
+
try {
|
|
33561
|
+
const result = await searchTasksByOwnerOperation({
|
|
33562
|
+
context: ctx,
|
|
33563
|
+
ownerReference: ownerRef
|
|
33564
|
+
});
|
|
33565
|
+
const bundle = buildSearchsetBundle(BASE_PATH.TASK, result.entries);
|
|
33566
|
+
return res.json(bundle);
|
|
33567
|
+
} catch (err) {
|
|
33568
|
+
return sendOperationOutcome500(
|
|
33569
|
+
res,
|
|
33570
|
+
err,
|
|
33571
|
+
"GET /Task?owner= search error:"
|
|
33572
|
+
);
|
|
33573
|
+
}
|
|
33574
|
+
}
|
|
33575
|
+
if (requesterRef !== void 0) {
|
|
33576
|
+
if (parseTypedReference(requesterRef) === void 0) {
|
|
33577
|
+
return sendInvalidSearch4005(
|
|
33578
|
+
res,
|
|
33579
|
+
`?requester must be a typed reference like "Practitioner/<id>"; got "${requesterRef}".`
|
|
33580
|
+
);
|
|
33581
|
+
}
|
|
33582
|
+
const ctx = req.openhiContext;
|
|
33583
|
+
try {
|
|
33584
|
+
const result = await searchTasksByRequesterOperation({
|
|
33585
|
+
context: ctx,
|
|
33586
|
+
requesterReference: requesterRef
|
|
33587
|
+
});
|
|
33588
|
+
const bundle = buildSearchsetBundle(BASE_PATH.TASK, result.entries);
|
|
33589
|
+
return res.json(bundle);
|
|
33590
|
+
} catch (err) {
|
|
33591
|
+
return sendOperationOutcome500(
|
|
33592
|
+
res,
|
|
33593
|
+
err,
|
|
33594
|
+
"GET /Task?requester= search error:"
|
|
33595
|
+
);
|
|
33596
|
+
}
|
|
33597
|
+
}
|
|
33056
33598
|
return handleListRoute({
|
|
33057
33599
|
req,
|
|
33058
33600
|
res,
|