Skip to content

Commit e59d583

Browse files
andreyorstryukzak
andcommitted
WIP mibrate to Bundle type
Co-authored-by: Aleksandr Penskoi <alexandr.penskoi@health-samurai.io>
1 parent 4b755ca commit e59d583

File tree

9 files changed

+153
-51
lines changed

9 files changed

+153
-51
lines changed

scripts/generate-types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ const builder = new APIBuilder()
1212
.treeShake({
1313
"hl7.fhir.r5.core": {
1414
"http://hl7.org/fhir/StructureDefinition/OperationOutcome": {},
15+
"http://hl7.org/fhir/StructureDefinition/Bundle": {},
16+
"http://hl7.org/fhir/StructureDefinition/Resource": {},
1517
},
1618
"org.sql-on-fhir.ig": {
1719
"https://sql-on-fhir.org/ig/StructureDefinition/ViewDefinition": {},

src/api/utils.tsx

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,40 @@ import { AidboxClientError } from "@health-samurai/aidbox-client";
66
import * as HSComp from "@health-samurai/react-components";
77
import type { MutationFunctionContext } from "@tanstack/react-query";
88

9-
export function toastOperationOutcome(oo: OperationOutcome) {
9+
export function parseOperationOutcome(
10+
oo: OperationOutcome,
11+
): { expression: string; diagnostics: string }[] {
1012
const issues = oo.issue;
11-
issues.forEach((o) => {
12-
if (typeof o !== "object" || o === null) {
13-
console.error("Invalid OperationOutcome error");
14-
return;
13+
14+
return issues.flatMap((issue) => {
15+
if (typeof issue !== "object" || issue === null) {
16+
return [];
1517
}
1618

1719
const expression =
18-
hasProperty(o, "expression") && typeof o.expression === "string"
19-
? o.expression
20+
hasProperty(issue, "expression") && typeof issue.expression === "string"
21+
? issue.expression
2022
: null;
2123

2224
const diagnostics =
23-
hasProperty(o, "diagnostics") && typeof o.diagnostics === "string"
24-
? o.diagnostics
25+
hasProperty(issue, "diagnostics") && typeof issue.diagnostics === "string"
26+
? issue.diagnostics
2527
: null;
2628

27-
if (expression === null && diagnostics === null) {
28-
console.error("Empty OperationOutcome error");
29-
return;
29+
if (expression === null || diagnostics === null) {
30+
return [];
3031
}
3132

33+
return { expression, diagnostics };
34+
});
35+
}
36+
37+
export function toastOperationOutcome(oo: OperationOutcome) {
38+
const issues = parseOperationOutcome(oo);
39+
if (issues.length === 0)
40+
throw new Error("Invalid OperationOutcome", { cause: oo });
41+
42+
issues.forEach(({ expression, diagnostics }) => {
3243
HSComp.toast.error(
3344
<div className="text-left">
3445
<b>{expression}</b>

src/components/ResourceEditor/action.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
import { defaultToastPlacement } from "@aidbox-ui/components/config";
2+
import type { Resource } from "@aidbox-ui/fhir-types/hl7-fhir-r5-core";
23
import type * as AidboxTypes from "@health-samurai/aidbox-client";
34
import * as HSComp from "@health-samurai/react-components";
45
import { useMutation } from "@tanstack/react-query";
56
import * as Router from "@tanstack/react-router";
67
import * as YAML from "js-yaml";
78
import * as Utils from "../../api/utils";
8-
import {
9-
createResource,
10-
deleteResource,
11-
type Resource,
12-
updateResource,
13-
} from "./api";
9+
import { createResource, deleteResource, updateResource } from "./api";
1410
import type { EditorMode } from "./types";
1511

1612
export const SaveButton = ({

src/components/ResourceEditor/api.ts

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
import type { AidboxClient } from "@health-samurai/aidbox-client";
2-
3-
export type Resource = {
4-
resourceType: string;
5-
id?: string;
6-
[key: string]: unknown;
7-
};
1+
import { parseOperationOutcome } from "@aidbox-ui/api/utils";
2+
import type { Bundle, Resource, OperationOutcome } from "@aidbox-ui/fhir-types/hl7-fhir-r5-core";
3+
import { isOperationOutcome } from "@aidbox-ui/fhir-types/hl7-fhir-r5-core";
4+
import type { AidboxClient, AidboxResponse } from "@health-samurai/aidbox-client";
85

96
export const fetchResource = async (
107
client: AidboxClient,
@@ -24,13 +21,6 @@ export const fetchResource = async (
2421
return raw;
2522
};
2623

27-
export interface HistoryBundle {
28-
resourceType: "Bundle";
29-
type: "history";
30-
total: number;
31-
entry: HistoryEntry[];
32-
}
33-
3424
export type HistoryEntryResource = {
3525
meta: {
3626
versionId: string;
@@ -49,22 +39,32 @@ export const fetchResourceHistory = async (
4939
client: AidboxClient,
5040
resourceType: string,
5141
id: string,
52-
) => {
53-
const raw = (
54-
await client.aidboxRequest<HistoryBundle>({
55-
method: "GET",
56-
url: `/fhir/${resourceType}/${id}/_history`,
57-
headers: {
58-
"Content-Type": "application/json",
59-
Accept: "application/json",
60-
},
61-
params: [
62-
["_page", "1"],
63-
["_count", "100"],
64-
],
65-
})
66-
).response.body;
67-
return raw as HistoryBundle;
42+
): Promise<Bundle> => {
43+
const res: AidboxResponse<Bundle> = await client.aidboxRequest<Bundle>({
44+
method: "GET",
45+
url: `/fhir/${resourceType}/${id}/_history`,
46+
headers: {
47+
"Content-Type": "application/json",
48+
Accept: "application/json",
49+
},
50+
params: [
51+
["_page", "1"],
52+
["_count", "100"],
53+
],
54+
});
55+
const response = res.response;
56+
57+
const body = response.body;
58+
59+
if (isOperationOutcome(body))
60+
throw new Error(
61+
parseOperationOutcome(body)
62+
.map(({ expression, diagnostics }) => `${expression}: ${diagnostics}`)
63+
.join("; "),
64+
{ cause: body },
65+
);
66+
67+
return body;
6868
};
6969

7070
export const createResource = async (

src/components/ResourceEditor/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
import type { Resource } from "@aidbox-ui/fhir-types/hl7-fhir-r5-core";
12
import * as HSComp from "@health-samurai/react-components";
23
import { useQuery } from "@tanstack/react-query";
34
import type * as Router from "@tanstack/react-router";
45
import * as YAML from "js-yaml";
56
import React from "react";
67
import { useAidboxClient } from "../../AidboxClient";
78
import { DeleteButton, SaveButton } from "./action";
8-
import { fetchResource, type Resource } from "./api";
9+
import { fetchResource } from "./api";
910
import { EditorTab } from "./editor-tab";
1011
import { type EditorMode, pageId, type ResourceEditorTab } from "./types";
1112
import { VersionsTab } from "./versions-tab";

src/components/ResourceEditor/versions-tab.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { Bundle } from "@aidbox-ui/fhir-types/hl7-fhir-r5-core";
12
import { DiffView } from "@git-diff-view/react";
23
import * as HSComp from "@health-samurai/react-components";
34
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
@@ -9,7 +10,6 @@ import { diff } from "../../utils/diff";
910
import { traverseTree } from "../../utils/tree-walker";
1011
import {
1112
fetchResourceHistory,
12-
type HistoryBundle,
1313
type HistoryEntry,
1414
type HistoryEntryResource,
1515
} from "./api";
@@ -428,7 +428,7 @@ const calculateAffectedAttributes = (
428428
export const VersionsTab = ({ id, resourceType }: VersionsTabProps) => {
429429
const client = useAidboxClient();
430430

431-
const [history, setHistory] = React.useState<HistoryBundle>();
431+
const [history, setHistory] = React.useState<Bundle>();
432432

433433
const {
434434
data: historyData,
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// WARNING: This file is autogenerated by @atomic-ehr/codegen.
2+
// GitHub: https://github.com/orgs/atomic-ehr/repositories
3+
// Any manual changes made to this file may be overwritten.
4+
5+
import type { BackboneElement } from "../hl7-fhir-r5-core/BackboneElement";
6+
import type { Identifier } from "../hl7-fhir-r5-core/Identifier";
7+
import type { Resource } from "../hl7-fhir-r5-core/Resource";
8+
import type { Signature } from "../hl7-fhir-r5-core/Signature";
9+
10+
export type { BackboneElement } from "../hl7-fhir-r5-core/BackboneElement";
11+
export type { Identifier } from "../hl7-fhir-r5-core/Identifier";
12+
export type { Signature } from "../hl7-fhir-r5-core/Signature";
13+
14+
export interface BundleEntry extends BackboneElement {
15+
fullUrl?: string;
16+
link?: BundleLink[];
17+
request?: BundleEntryRequest;
18+
resource?: Resource;
19+
response?: BundleEntryResponse;
20+
search?: BundleEntrySearch;
21+
}
22+
23+
export interface BundleEntryRequest extends BackboneElement {
24+
ifMatch?: string;
25+
ifModifiedSince?: string;
26+
ifNoneExist?: string;
27+
ifNoneMatch?: string;
28+
method: "GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "PATCH";
29+
url: string;
30+
}
31+
32+
export interface BundleEntryResponse extends BackboneElement {
33+
etag?: string;
34+
lastModified?: string;
35+
location?: string;
36+
outcome?: Resource;
37+
status: string;
38+
}
39+
40+
export interface BundleEntrySearch extends BackboneElement {
41+
mode?: "match" | "include" | "outcome";
42+
score?: number;
43+
}
44+
45+
export interface BundleLink extends BackboneElement {
46+
relation: string;
47+
url: string;
48+
}
49+
50+
// CanonicalURL: http://hl7.org/fhir/StructureDefinition/Bundle
51+
export interface Bundle extends Resource {
52+
resourceType: "Bundle";
53+
54+
entry?: BundleEntry[];
55+
identifier?: Identifier;
56+
issues?: Resource;
57+
link?: BundleLink[];
58+
signature?: Signature;
59+
timestamp?: string;
60+
_timestamp?: Element;
61+
total?: number;
62+
_total?: Element;
63+
type:
64+
| "document"
65+
| "message"
66+
| "transaction"
67+
| "transaction-response"
68+
| "batch"
69+
| "batch-response"
70+
| "history"
71+
| "searchset"
72+
| "collection"
73+
| "subscription-notification";
74+
_type?: Element;
75+
}
76+
export const isBundle = (resource: unknown): resource is Bundle => {
77+
return (
78+
resource !== null &&
79+
typeof resource === "object" &&
80+
(resource as { resourceType: string }).resourceType === "Bundle"
81+
);
82+
};

src/fhir-types/hl7-fhir-r5-core/Resource.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export type { Meta } from "../hl7-fhir-r5-core/Meta";
1111
// CanonicalURL: http://hl7.org/fhir/StructureDefinition/Resource
1212
export interface Resource extends Base {
1313
resourceType:
14+
| "Bundle"
1415
| "CanonicalResource"
1516
| "DomainResource"
1617
| "OperationOutcome"

src/fhir-types/hl7-fhir-r5-core/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ export type { Availability } from "./Availability";
66
export type { BackboneElement } from "./BackboneElement";
77
export type { BackboneType } from "./BackboneType";
88
export type { Base } from "./Base";
9+
export type {
10+
Bundle,
11+
BundleEntry,
12+
BundleEntryRequest,
13+
BundleEntryResponse,
14+
BundleEntrySearch,
15+
BundleLink,
16+
} from "./Bundle";
17+
export { isBundle } from "./Bundle";
918
export type { CanonicalResource } from "./CanonicalResource";
1019
export { isCanonicalResource } from "./CanonicalResource";
1120
export type { CodeableConcept } from "./CodeableConcept";

0 commit comments

Comments
 (0)