Skip to content

Commit 25d4901

Browse files
feat(typescript): add .where() for conditional methods
1 parent 240c5d9 commit 25d4901

File tree

2 files changed

+130
-93
lines changed

2 files changed

+130
-93
lines changed

resources/sdk/typescript/index.ts

+129-92
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,13 @@ export type CreateQueryBody = {
8989
"count-query": string;
9090
};
9191

92-
type Link = { relation: string; url: string };
93-
92+
// TODO: change to FHIR Bundle
9493
export type BaseResponseResources<T extends keyof ResourceTypeMap> = {
9594
meta: { versionId: string };
9695
type: string;
9796
resourceType: string;
9897
total: number;
99-
link: Link[];
98+
link: { relation: string; url: string }[];
10099
entry?: { resource: ResourceTypeMap[T] }[];
101100
"query-timeout": number;
102101
"query-time": number;
@@ -581,6 +580,8 @@ const resourceOwnerLogout =
581580
auth.storage.set(undefined);
582581
};
583582

583+
export type ProcessableBundle = Omit<ResourceTypeMap['Bundle'], 'type'> & { type: 'batch' | 'transaction' }
584+
584585
export type BasicAuthorization = {
585586
method: "basic";
586587
credentials: { username: string; password: string };
@@ -668,97 +669,85 @@ export class Client<T extends BasicAuthorization | ResourceOwnerAuthorization> {
668669
head: request(this.client.head),
669670
});
670671

671-
672-
673-
// request = <T extends keyof ResourceTypeMap>(params: RequestWithData<T> | RequestWithoutData<T>) => {
674-
675-
// }
676-
677-
678672
private search = <T extends keyof ResourceTypeMap>(resourceName: T) => {
679-
return new GetResources(this.client, resourceName);
673+
return new GetResources((searchParams: URLSearchParams) => {
674+
return this.client.get(buildResourceUrl(resourceName), { searchParams })
675+
}, resourceName);
680676
}
681677

682678
resource = {
683-
processBundle: (data: ResourceTypeMap['Bundle']): Promise<ResourceTypeMap['Bundle']> => {
679+
processBundle: (data: ProcessableBundle): Promise<ResourceTypeMap['Bundle']> => {
684680
return this.client
685681
.post(buildResourceUrl(''), { json: data })
686682
.json<ResourceTypeMap['Bundle']>();
687683
},
688684
conditionalUpdate: <T extends keyof ResourceTypeMap>(
689685
resourceName: T,
690-
search: string,
691686
input: PartialResourceBody<T>
692687
) => {
693-
return this.client
694-
.patch(`${buildResourceUrl(resourceName)}?${search}`, { json: input })
695-
.json<BaseResponseResource<T>>();
688+
return new FindBeforeAction((searchParams: URLSearchParams) => {
689+
return this.client.patch(buildResourceUrl(resourceName), { json: input, searchParams })
690+
}, resourceName)
696691
},
697692
conditionalCreate: <T extends keyof ResourceTypeMap>(
698693
resourceName: T,
699-
search: string,
700694
input: PartialResourceBody<T>
701695
) => {
702-
return this.client
703-
.post(`${buildResourceUrl(resourceName)}?${search}`, { json: input })
704-
.json<BaseResponseResource<T>>();
696+
return new FindBeforeAction((searchParams: URLSearchParams) => {
697+
return this.client.post(buildResourceUrl(resourceName), { json: input, searchParams })
698+
}, resourceName)
705699
},
706700
conditionalOverride: <T extends keyof ResourceTypeMap>(
707701
resourceName: T,
708-
search: string,
709702
input: PartialResourceBody<T>
710703
) => {
711-
return this.client
712-
.put(`${buildResourceUrl(resourceName)}?${search}`, { json: input })
713-
.json<BaseResponseResource<T>>();
704+
return new FindBeforeAction((searchParams: URLSearchParams) => {
705+
return this.client.put(buildResourceUrl(resourceName), { json: input, searchParams })
706+
}, resourceName)
714707
},
715-
conditionalDelete: <T extends keyof ResourceTypeMap>(
716-
resourceName: T,
717-
search: string
718-
) => {
719-
return this.client
720-
.delete(`${buildResourceUrl(resourceName)}?${search}`)
721-
.json<BaseResponseResource<T>>();
708+
// TODO: no body in response if resource doesn't exist -> lead to parsing error
709+
conditionalDelete: <T extends keyof ResourceTypeMap>(resourceName: T) => {
710+
return new FindBeforeAction((searchParams: URLSearchParams) => {
711+
return this.client.delete(buildResourceUrl(resourceName), { searchParams })
712+
}, resourceName)
722713
},
723714
search: this.search,
724715
list: this.search,
725-
get: async <T extends keyof ResourceTypeMap>(
726-
resourceName: T,
727-
id: string,
728-
): Promise<BaseResponseResource<T>> => {
716+
get: async <T extends keyof ResourceTypeMap>(resourceName: T, id: string): Promise<ResourceTypeMap[T]> => {
729717
return this.client
730718
.get(buildResourceUrl(resourceName, id))
731-
.json<BaseResponseResource<T>>();
719+
.json<ResourceTypeMap[T]>();
732720
},
733-
delete: <T extends keyof ResourceTypeMap>(
734-
resourceName: T,
735-
id: string,
736-
) => {
721+
delete: <T extends keyof ResourceTypeMap>(resourceName: T, id: string): Promise<ResourceTypeMap[T]> => {
737722
return this.client
738-
.delete(buildResourceUrl(resourceName, id));
723+
.delete(buildResourceUrl(resourceName, id))
724+
.json<ResourceTypeMap[T]>();
739725
},
740726
update: <T extends keyof ResourceTypeMap>(
741727
resourceName: T,
742728
id: string,
743729
input: PartialResourceBody<T>,
744-
) => {
730+
): Promise<ResourceTypeMap[T]> => {
745731
return this.client
746-
.patch(buildResourceUrl(resourceName, id), { json: input });
732+
.patch(buildResourceUrl(resourceName, id), { json: input })
733+
.json<ResourceTypeMap[T]>();
747734
},
748735
create: <T extends keyof ResourceTypeMap>(
749736
resourceName: T,
750737
input: SetOptional<ResourceTypeMap[T] & { resourceType: string }, 'resourceType'>,
751738
) => {
752739
return this.client
753-
.post(buildResourceUrl(resourceName), { json: input });
740+
.post(buildResourceUrl(resourceName), { json: input })
741+
.json<ResourceTypeMap[T]>();
754742
},
755743
override: <T extends keyof ResourceTypeMap>(
756744
resourceName: T,
757745
id: string,
758746
input: PartialResourceBody<T>,
759-
) => {
747+
): Promise<ResourceTypeMap[T]> => {
760748
return this.client
761-
.put(buildResourceUrl(resourceName, id), { json: input });
749+
.put(buildResourceUrl(resourceName, id), { json: input })
750+
.json<ResourceTypeMap[T]>();
762751
},
763752
};
764753

@@ -827,19 +816,16 @@ export class Client<T extends BasicAuthorization | ResourceOwnerAuthorization> {
827816
}
828817
}
829818

830-
export class GetResources<
831-
T extends keyof ResourceTypeMap,
832-
R extends ResourceTypeMap[T],
833-
> implements PromiseLike<BaseResponseResources<T>>
819+
export class GetResources<T extends keyof ResourceTypeMap> implements PromiseLike<BaseResponseResources<T>>
834820
{
835-
private searchParamsObject: URLSearchParams;
821+
protected searchParamsObject: URLSearchParams;
836822
resourceName: T;
837-
client: HttpClientInstance;
823+
fun: (params: URLSearchParams) => ResponsePromise;
838824

839-
constructor(client: HttpClientInstance, resourceName: T) {
825+
constructor(fun: (params: URLSearchParams) => ResponsePromise, resourceName: T) {
840826
this.searchParamsObject = new URLSearchParams();
841827
this.resourceName = resourceName;
842-
this.client = client;
828+
this.fun = fun;
843829
}
844830

845831
where<
@@ -857,32 +843,40 @@ export class GetResources<
857843
where<
858844
K extends keyof SearchParams[T],
859845
SP extends SearchParams[T][K],
860-
PR extends SP extends number ? Prefix : never,
861846
>(key: K | string, value: SP | SP[], prefix?: Prefix | never): this {
862-
if (Array.isArray(value)) {
863-
const val = value as SP[];
864-
if (prefix) {
865-
if (prefix === "eq") {
866-
this.searchParamsObject.append(key.toString(), val.join(","));
867-
return this;
868-
}
869-
870-
val.forEach((item) => {
871-
this.searchParamsObject.append(key.toString(), `${prefix}${item}`);
872-
});
873-
874-
return this;
875-
}
876-
877-
const queryValues = val.join(",");
878-
this.searchParamsObject.append(key.toString(), queryValues);
847+
if (!Array.isArray(value)) {
848+
this.searchParamsObject.append(key.toString(), `${prefix || ''}${value}`);
849+
return this;
850+
}
879851

852+
if (prefix && prefix === 'eq') {
853+
this.searchParamsObject.append(key.toString(), value.join(','));
880854
return this;
881855
}
882-
const queryValue = `${prefix ?? ""}${value}`;
856+
857+
if (prefix) {
858+
value.forEach((item) => this.searchParamsObject.append(key.toString(), `${prefix}${item}`));
859+
return this;
860+
}
861+
862+
this.searchParamsObject.append(key.toString(), value.join(','));
863+
return this;
864+
}
883865

884-
this.searchParamsObject.append(key.toString(), queryValue);
885-
return this;
866+
async then<TResult1 = BaseResponseResources<T>, TResult2 = never>(
867+
onfulfilled?: ((value: BaseResponseResources<T>) => PromiseLike<TResult1> | TResult1),
868+
onrejected?: ((reason: unknown) => PromiseLike<TResult2> | TResult2)
869+
): Promise<TResult1 | TResult2> {
870+
return this.fun(this.searchParamsObject)
871+
.then((response) => {
872+
return onfulfilled
873+
? response.json<BaseResponseResources<T>>().then((data) => onfulfilled(data))
874+
: response.json();
875+
}, onrejected);
876+
}
877+
878+
async catch(onRejected: (reason: unknown) => Promise<unknown>) {
879+
return this.then(undefined, onRejected);
886880
}
887881

888882
contained(
@@ -910,7 +904,7 @@ export class GetResources<
910904
return this;
911905
}
912906

913-
elements(args: ElementsParams<T, R>) {
907+
elements(args: ElementsParams<T, ResourceTypeMap[T]>) {
914908
const queryValue = args.join(",");
915909

916910
this.searchParamsObject.set("_elements", queryValue);
@@ -943,27 +937,70 @@ export class GetResources<
943937

944938
return this;
945939
}
940+
}
946941

947-
then<TResult1 = BaseResponseResources<T>, TResult2 = never>(
948-
onfulfilled?:
949-
| ((value: BaseResponseResources<T>) => PromiseLike<TResult1> | TResult1)
950-
| undefined
951-
| null,
952-
onrejected?:
953-
| ((reason: unknown) => PromiseLike<TResult2> | TResult2)
954-
| undefined
955-
| null,
956-
): PromiseLike<TResult1 | TResult2> {
957-
return this.client
958-
.get(buildResourceUrl(this.resourceName), {
959-
searchParams: this.searchParamsObject,
960-
})
942+
export class FindBeforeAction<T extends keyof ResourceTypeMap> implements PromiseLike<BaseResponseResource<T>>
943+
{
944+
protected searchParamsObject: URLSearchParams;
945+
resourceName: T;
946+
fun: (params: URLSearchParams) => ResponsePromise;
947+
948+
constructor(fun: (params: URLSearchParams) => ResponsePromise, resourceName: T) {
949+
this.searchParamsObject = new URLSearchParams();
950+
this.resourceName = resourceName;
951+
this.fun = fun;
952+
}
953+
954+
where<
955+
K extends keyof SearchParams[T],
956+
SP extends SearchParams[T][K],
957+
PR extends PrefixWithArray,
958+
>(key: K | string, value: SP | SP[], prefix?: PR): this;
959+
960+
where<
961+
K extends keyof SearchParams[T],
962+
SP extends SearchParams[T][K],
963+
PR extends Exclude<Prefix, PrefixWithArray>,
964+
>(key: K | string, value: SP, prefix?: PR): this;
965+
966+
where<
967+
K extends keyof SearchParams[T],
968+
SP extends SearchParams[T][K],
969+
>(key: K | string, value: SP | SP[], prefix?: Prefix | never): this {
970+
if (!Array.isArray(value)) {
971+
this.searchParamsObject.append(key.toString(), `${prefix || ''}${value}`);
972+
return this;
973+
}
974+
975+
if (prefix && prefix === 'eq') {
976+
this.searchParamsObject.append(key.toString(), value.join(','));
977+
return this;
978+
}
979+
980+
if (prefix) {
981+
value.forEach((item) => this.searchParamsObject.append(key.toString(), `${prefix}${item}`));
982+
return this;
983+
}
984+
985+
this.searchParamsObject.append(key.toString(), value.join(','));
986+
return this;
987+
}
988+
989+
async then<TResult1 = BaseResponseResource<T>, TResult2 = never>(
990+
onfulfilled?: ((value: BaseResponseResource<T>) => PromiseLike<TResult1> | TResult1),
991+
onrejected?: ((reason: unknown) => PromiseLike<TResult2> | TResult2)
992+
): Promise<TResult1 | TResult2> {
993+
return this.fun(this.searchParamsObject)
961994
.then((response) => {
962995
return onfulfilled
963-
? response.json<BaseResponseResources<T>>().then((data) => onfulfilled(data))
964-
: (response.json() as TResult1);
996+
? response.json<BaseResponseResource<T>>().then((data) => onfulfilled(data))
997+
: response.json();
965998
}, onrejected);
966999
}
1000+
1001+
async catch(onRejected: (reason: unknown) => Promise<unknown>) {
1002+
return this.then(undefined, onRejected);
1003+
}
9671004
}
9681005

9691006
type EventType =

resources/sdk/typescript/types/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export interface SubsSubscription {
1414
export interface ResourceTypeMap {
1515
'placeholder-1': object;
1616
'placeholder-2': object;
17-
"Bundle": object,
17+
"Bundle": { entry?: [] },
1818
"Patient": object
1919
}
2020

0 commit comments

Comments
 (0)