Skip to content

Commit 4ec6ded

Browse files
authored
feat: new hooks paramsFromClient/paramsForServer (#24)
1 parent b8d11ab commit 4ec6ded

6 files changed

Lines changed: 211 additions & 0 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ npm i feathers-utils
2525
- `createRelated`: simply create related items from a hook.
2626
- `forEach`
2727
- `onDelete`: simply remove/set null related items from a hook.
28+
- `paramsForServer`
29+
- `paramsFromClient`
2830
- `parseFields`
2931
- `removeRelated`: simple remove related items from a hook. Basically `cascade` at feathers level.
3032
- `runPerItem`: run a function for every item. Meant for `multi:true`.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const FROM_CLIENT_FOR_SERVER_DEFAULT_KEY = "_$client" as const;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./paramsForServer";
2+
export * from "./paramsFromClient";
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import type { HookContext } from "@feathersjs/feathers";
2+
import { FROM_CLIENT_FOR_SERVER_DEFAULT_KEY } from "./common";
3+
4+
export function defineParamsForServer(keyToHide: string) {
5+
return function paramsForServer(...whitelist: string[]) {
6+
return <H extends HookContext>(context: H) => {
7+
// clone params on demand
8+
let clonedParams;
9+
10+
Object.keys(context.params).forEach((key) => {
11+
if (key === "query") {
12+
return;
13+
}
14+
15+
if (whitelist.includes(key)) {
16+
if (!clonedParams) {
17+
clonedParams = {
18+
...context.params,
19+
query: {
20+
...context.params.query,
21+
},
22+
};
23+
}
24+
25+
if (!clonedParams.query[keyToHide]) {
26+
clonedParams.query[keyToHide] = {};
27+
}
28+
29+
clonedParams.query[keyToHide][key] = clonedParams[key];
30+
delete clonedParams[key];
31+
}
32+
});
33+
34+
if (clonedParams) {
35+
context.params = clonedParams;
36+
}
37+
38+
return context;
39+
};
40+
};
41+
}
42+
43+
/**
44+
* a hook to move params to query._$client
45+
* the server only receives 'query' from params. All other params are ignored.
46+
* So, to use `$populateParams` on the server, we need to move the params to query._$client
47+
* the server will move them back to params
48+
*/
49+
export const paramsForServer = defineParamsForServer(
50+
FROM_CLIENT_FOR_SERVER_DEFAULT_KEY,
51+
);
52+
53+
if (import.meta.vitest) {
54+
const { it, expect } = import.meta.vitest;
55+
56+
it("should move params to query._$client", () => {
57+
expect(
58+
paramsForServer(
59+
"a",
60+
"b",
61+
)({
62+
params: {
63+
a: 1,
64+
b: 2,
65+
query: {},
66+
},
67+
} as HookContext),
68+
).toEqual({
69+
params: {
70+
query: {
71+
_$client: {
72+
a: 1,
73+
b: 2,
74+
},
75+
},
76+
},
77+
});
78+
});
79+
80+
it("should move params to query._$client and leave remaining", () => {
81+
expect(
82+
paramsForServer("a")({
83+
params: {
84+
a: 1,
85+
b: 2,
86+
query: {},
87+
},
88+
} as HookContext),
89+
).toEqual({
90+
params: {
91+
b: 2,
92+
query: {
93+
_$client: {
94+
a: 1,
95+
},
96+
},
97+
},
98+
});
99+
});
100+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import type { HookContext } from "@feathersjs/feathers";
2+
import { FROM_CLIENT_FOR_SERVER_DEFAULT_KEY } from "./common";
3+
4+
export function defineParamsFromClient(keyToHide: string) {
5+
return function paramsFromClient(
6+
...whitelist: string[]
7+
): (context: HookContext) => HookContext {
8+
return (context: HookContext): HookContext => {
9+
if (
10+
!context.params?.query?.[keyToHide] ||
11+
typeof context.params.query[keyToHide] !== "object"
12+
) {
13+
return context;
14+
}
15+
16+
const params = {
17+
...context.params,
18+
query: {
19+
...context.params.query,
20+
[keyToHide]: {
21+
...context.params.query[keyToHide],
22+
},
23+
},
24+
};
25+
26+
const client = params.query[keyToHide];
27+
28+
whitelist.forEach((key) => {
29+
if (key in client) {
30+
params[key] = client[key];
31+
delete client[key];
32+
}
33+
});
34+
35+
if (Object.keys(client).length === 0) {
36+
delete params.query[keyToHide];
37+
}
38+
39+
context.params = params;
40+
41+
return context;
42+
};
43+
};
44+
}
45+
46+
export const paramsFromClient = defineParamsFromClient(
47+
FROM_CLIENT_FOR_SERVER_DEFAULT_KEY,
48+
);
49+
50+
if (import.meta.vitest) {
51+
const { it, expect } = import.meta.vitest;
52+
53+
it("should move params to query._$client", () => {
54+
expect(
55+
paramsFromClient(
56+
"a",
57+
"b",
58+
)({
59+
params: {
60+
query: {
61+
_$client: {
62+
a: 1,
63+
b: 2,
64+
},
65+
c: 3,
66+
},
67+
},
68+
} as HookContext),
69+
).toEqual({
70+
params: {
71+
a: 1,
72+
b: 2,
73+
query: {
74+
c: 3,
75+
},
76+
},
77+
});
78+
});
79+
80+
it("should move params to query._$client and leave remaining", () => {
81+
expect(
82+
paramsFromClient("a")({
83+
params: {
84+
query: {
85+
_$client: {
86+
a: 1,
87+
b: 2,
88+
},
89+
c: 3,
90+
},
91+
},
92+
} as HookContext),
93+
).toEqual({
94+
params: {
95+
a: 1,
96+
query: {
97+
_$client: {
98+
b: 2,
99+
},
100+
c: 3,
101+
},
102+
},
103+
});
104+
});
105+
}

src/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export * from "./parseFields";
66
export * from "./removeRelated";
77
export * from "./runPerItem";
88
export * from "./setData";
9+
export * from "./from-client-for-server";

0 commit comments

Comments
 (0)