Skip to content

Commit 1a600b1

Browse files
committed
Enhance addItemToList to sort items by with nested paths & unwrap secured values / lists
This allows `engagements.total` to match the correct value. Along with `engagements.language.name`, which actually traverses as `engagements.items.language.value.name.value`
1 parent 25acf40 commit 1a600b1

File tree

3 files changed

+47
-11
lines changed

3 files changed

+47
-11
lines changed

src/api/caching/lists/addItemToList.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import {
33
MutationUpdaterFunction,
44
Reference,
55
} from '@apollo/client';
6-
import { sortBy } from '@seedcompany/common';
6+
import { isObjectLike, sortBy } from '@seedcompany/common';
77
import { Except } from 'type-fest';
8-
import { unwrapSecured } from '~/common';
8+
import { unwrapSecuredEdge } from '~/common';
99
import { modifyChangesetDiff } from '../../changesets';
1010
import type { Entity } from '../../schema';
1111
import type { Order } from '../../schema.graphql';
@@ -96,10 +96,19 @@ export const addItemToList =
9696
if (sort && order) {
9797
newList = sortBy(newList, [
9898
(ref) => {
99-
const field = readField(sort, ref);
100-
const fieldVal = unwrapSecured(field);
99+
const fieldVal = sort.split('.').reduce((val, key, i, parts) => {
100+
if (!val) {
101+
return val;
102+
}
103+
const raw = readField(key, val);
104+
const nextKey = parts[i + 1];
105+
if (nextKey && isObjectLike(raw) && nextKey in raw) {
106+
return raw;
107+
}
108+
return unwrapSecuredEdge(raw);
109+
}, ref as any);
101110
// Unsafely assume this value is sortable
102-
return fieldVal as any;
111+
return fieldVal;
103112
},
104113
order.toLowerCase() as Lowercase<Order>,
105114
]);

src/common/secured.ts

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Nil } from '@seedcompany/common';
1+
import { isObjectLike, Nil } from '@seedcompany/common';
22
import { ConditionalKeys } from 'type-fest';
33

44
interface Readable {
@@ -21,16 +21,43 @@ export const isSecured = <T>(value: unknown): value is SecuredProp<T> =>
2121
'canEdit' in value &&
2222
'canRead' in value;
2323

24+
/**
25+
* @deprecated unsafely assumes canRead/Edit
26+
*/
2427
export const unwrapSecured = <T>(value: T) =>
25-
(!!value &&
26-
typeof value === 'object' &&
27-
'__typename' in value &&
28-
typeof value.__typename === 'string' &&
28+
(isObjectLike(value) &&
29+
hasTypename(value) &&
2930
value.__typename.startsWith('Secured') &&
3031
'value' in value
3132
? value.value
3233
: value) as T extends SecuredProp<infer U> ? U : T;
3334

35+
export const unwrapSecuredEdge = <T>(
36+
value: T
37+
): T extends { __typename: `Secured${string}` }
38+
? T extends { value: infer U }
39+
? U
40+
: T extends { items: infer U }
41+
? U
42+
: T
43+
: T => {
44+
if (
45+
isObjectLike(value) &&
46+
hasTypename(value) &&
47+
value.__typename.startsWith('Secured')
48+
) {
49+
return 'value' in value
50+
? value.value
51+
: 'items' in value && Array.isArray(value.items)
52+
? value.items
53+
: (value as any);
54+
}
55+
return value as any;
56+
};
57+
58+
const hasTypename = (value: object): value is { __typename: string } =>
59+
'__typename' in value && typeof value.__typename === 'string';
60+
3461
/**
3562
* Can the user read any of the fields of this object?
3663
* If no keys are specified all "secured" properties will be checked.

src/components/ProjectDataGrid/ProjectColumns.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export const ProjectColumns: Array<GridColDef<Project>> = [
6767
width: 160,
6868
},
6969
{
70-
field: 'engagements',
70+
field: 'engagements.total',
7171
type: 'number',
7272
valueGetter: (_, { engagements }) => engagements.total,
7373
filterable: false,

0 commit comments

Comments
 (0)