Skip to content

Commit

Permalink
feat!: get public items by tag hook, get public member, export api, u…
Browse files Browse the repository at this point in the history
…se only named export
  • Loading branch information
pyphilia committed Sep 27, 2021
1 parent 670f4b8 commit ec515e0
Show file tree
Hide file tree
Showing 11 changed files with 1,288 additions and 1,077 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
"module": "dist/index.modern.js",
"source": "src/index.ts",
"dependencies": {
"axios": "0.21.4",
"http-status-codes": "2.1.4",
"immutable": "4.0.0-rc.12",
"js-cookie": "3.0.0",
"node-fetch": "2.6.1",
"qs": "6.10.1",
"react-query": "3.16.0",
"uuid": "8.3.2"
Expand Down Expand Up @@ -49,6 +49,7 @@
"jest": "27.0.6",
"microbundle-crl": "0.13.11",
"nock": "13.1.0",
"node-fetch": "2.6.1",
"npm-run-all": "4.1.5",
"prettier": "2.2.1",
"pretty-quick": "3.1.0",
Expand Down
55 changes: 38 additions & 17 deletions src/api/item.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { StatusCodes } from 'http-status-codes';
import axios from 'axios';
import {
buildCopyItemRoute,
buildCopyItemsRoute,
Expand All @@ -11,6 +12,7 @@ import {
buildGetItemsRoute,
buildGetPublicChildrenRoute,
buildGetPublicItemRoute,
buildGetPublicItemsWithTag,
buildGetS3MetadataRoute,
buildMoveItemRoute,
buildMoveItemsRoute,
Expand All @@ -33,22 +35,31 @@ import {
import { getParentsIdsFromPath } from '../utils/item';
import { ExtendedItem, Item, QueryClientConfig, UUID } from '../types';

export const getItem = async (id: UUID, { API_HOST }: QueryClientConfig) => {
let res = await fetch(`${API_HOST}/${buildGetItemRoute(id)}`, DEFAULT_GET);

// try to fetch public items if cannot access privately
if (res.status === StatusCodes.UNAUTHORIZED) {
res = await fetch(
`${API_HOST}/${buildGetPublicItemRoute(id)}`,
DEFAULT_GET,
).then(failOnError);
}
if (!res.ok) {
throw new Error(res.statusText);
}
const item = await res.json();
return item;
};
export const getItem = (
id: UUID,
options: { withMemberships?: boolean },
{ API_HOST }: QueryClientConfig,
) =>
axios
.get(`${API_HOST}/${buildGetItemRoute(id)}`, {
withCredentials: true,
})
.then(({ data }) => data)
.catch((e) => {
if (e.response.status === StatusCodes.UNAUTHORIZED) {
// try to fetch public items if cannot access privately
return axios
.get(`${API_HOST}/${buildGetPublicItemRoute(id, options)}`, {
withCredentials: true,
})
.then(({ data: d }) => d)
.catch(() => {
throw new Error(e.response?.statusText);
});
}

throw new Error(e.response?.statusText);
});

export const getItems = async (
ids: UUID[],
Expand Down Expand Up @@ -159,7 +170,7 @@ export const getParents = async (
) => {
const parentIds = getParentsIdsFromPath(path, { ignoreSelf: true });
if (parentIds.length) {
return Promise.all(parentIds.map((id) => getItem(id, config)));
return Promise.all(parentIds.map((id) => getItem(id, {}, config)));
}
return [];
};
Expand Down Expand Up @@ -315,3 +326,13 @@ export const recycleItems = async (

return res.ok;
};

export const getPublicItemsWithTag = async (
options: { tagId: UUID; withMemberships: boolean },
{ API_HOST }: QueryClientConfig,
) =>
axios
.get(`${API_HOST}/${buildGetPublicItemsWithTag(options)}`, {
withCredentials: true,
})
.then(({ data }) => data);
73 changes: 60 additions & 13 deletions src/api/member.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { StatusCodes } from 'http-status-codes';
import axios from 'axios';
import { failOnError, DEFAULT_GET, DEFAULT_PATCH } from './utils';
import {
buildGetMemberBy,
buildGetMember,
GET_CURRENT_MEMBER_ROUTE,
buildPatchMember,
buildGetMembersRoute,
buildGetPublicMembers,
buildGetPublicMember,
} from './routes';
import { Member, QueryClientConfig, UUID } from '../types';
import { SIGNED_OUT_USER } from '../config/constants';
Expand All @@ -24,24 +27,68 @@ export const getMemberBy = async (
export const getMember = async (
{ id }: { id: UUID },
{ API_HOST }: QueryClientConfig,
) => {
const res = await fetch(`${API_HOST}/${buildGetMember(id)}`, {
...DEFAULT_GET,
}).then(failOnError);
) =>
axios
.get(`${API_HOST}/${buildGetMember(id)}`, {
withCredentials: true,
})
.then(({ data }) => data)
.catch((e) => {
if (e.response.status === StatusCodes.UNAUTHORIZED) {
// try to fetch public items if cannot access privately
return axios
.get(`${API_HOST}/${buildGetPublicMember(id)}`, {
withCredentials: true,
})
.then(({ data: d }) => d)
.catch(() => {
throw new Error(e.response?.statusText);
});
}

return res.json();
};
throw new Error(e.response?.statusText);
});

// {
// const res = await fetch(`${API_HOST}/${buildGetMember(id)}`, {
// ...DEFAULT_GET,
// }).then(failOnError);

// return res.json();
// };

export const getMembers = async (
export const getMembers = (
{ ids }: { ids: UUID[] },
{ API_HOST }: QueryClientConfig,
) => {
const res = await fetch(`${API_HOST}/${buildGetMembersRoute(ids)}`, {
...DEFAULT_GET,
}).then(failOnError);
) =>
axios
.get(`${API_HOST}/${buildGetMembersRoute(ids)}`, {
withCredentials: true,
})
.then(({ data }) => data)
.catch((e) => {
if (e.response.status === StatusCodes.UNAUTHORIZED) {
// try to fetch public items if cannot access privately
return axios
.get(`${API_HOST}/${buildGetPublicMembers(ids)}`, {
withCredentials: true,
})
.then(({ data: d }) => d)
.catch(() => {
throw new Error(e.response?.statusText);
});
}

return res.json();
};
throw new Error(e.response?.statusText);
});

// {
// const res = await fetch(`${API_HOST}/${buildGetMembersRoute(ids)}`, {
// ...DEFAULT_GET,
// }).then(failOnError);

// return res.json();
// };

export const getCurrentMember = async ({ API_HOST }: QueryClientConfig) => {
const res = await fetch(`${API_HOST}/${GET_CURRENT_MEMBER_ROUTE}`, {
Expand Down
16 changes: 15 additions & 1 deletion src/api/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ export const buildGetChildrenRoute = (id: UUID, ordered: boolean) =>
{ addQueryPrefix: true },
)}`;
export const buildGetItemRoute = (id: UUID) => `${ITEMS_ROUTE}/${id}`;
export const buildGetPublicItemRoute = (id: UUID) => `p/${ITEMS_ROUTE}/${id}`;
export const buildGetPublicItemRoute = (
id: UUID,
options: { withMemberships?: boolean },
) => `p/${ITEMS_ROUTE}/${id}${qs.stringify(options, { addQueryPrefix: true })}`;
export const buildGetPublicChildrenRoute = (id: UUID, ordered: boolean) =>
`p/${ITEMS_ROUTE}/${id}/children${qs.stringify(
{ ordered },
Expand Down Expand Up @@ -103,6 +106,14 @@ export const buildRecycleItemsRoute = (ids: UUID[]) =>
{ id: ids },
{ arrayFormat: 'repeat' },
)}`;
export const buildGetPublicItemsWithTag = (options: {
tagId: UUID;
withMemberships: boolean;
}) => `p/${ITEMS_ROUTE}?${qs.stringify(options)}`;
export const buildGetPublicMembers = (ids: UUID[]) =>
`p/${MEMBERS_ROUTE}?${qs.stringify({ id: ids }, { arrayFormat: 'repeat' })}`;

export const buildGetPublicMember = (id: UUID) => `p/${MEMBERS_ROUTE}/${id}`;

export const API_ROUTES = {
ITEMS_ROUTE,
Expand Down Expand Up @@ -150,4 +161,7 @@ export const API_ROUTES = {
buildPostItemChatMessageRoute,
buildRecycleItemRoute,
buildRecycleItemsRoute,
buildGetPublicItemsWithTag,
buildGetPublicMember,
buildGetPublicMembers,
};
9 changes: 8 additions & 1 deletion src/config/keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const buildItemChildrenKey = (id?: UUID) => [ITEMS_KEY, id, 'children'];
export const SHARED_ITEMS_KEY = 'shared';
export const CURRENT_MEMBER_KEY = 'currentMember';
export const MEMBERS_KEY = 'members';
export const buildMemberKey = (id: UUID) => [MEMBERS_KEY, id];
export const buildMemberKey = (id?: UUID) => [MEMBERS_KEY, id];
export const buildMembersKey = (ids: UUID[]) => [
MEMBERS_KEY,
hashItemsIds(ids),
Expand All @@ -27,6 +27,7 @@ export const buildItemMembershipsKey = (id?: UUID) => [
'memberships',
];
export const buildItemLoginKey = (id?: UUID) => [ITEMS_KEY, id, 'login'];
export const TAGS = 'tags';
export const ITEM_TAGS_KEY = 'itemTags';
export const buildItemTagsKey = (id?: UUID) => [ITEMS_KEY, id, 'tags'];
export const buildFileContentKey = (id?: UUID) => [ITEMS_KEY, id, 'content'];
Expand All @@ -35,6 +36,12 @@ export const buildS3FileContentKey = (id?: UUID) => [ITEMS_KEY, id, 'content'];
export const ITEM_FLAGS_KEY = 'itemFlags';
export const buildItemFlagsKey = (id: UUID) => [ITEMS_KEY, id, 'flags'];

export const buildPublicItemsWithTagKey = (id?: UUID) => [
ITEMS_KEY,
ITEM_TAGS_KEY,
id,
];

export const MUTATION_KEYS = {
POST_ITEM: 'postItem',
EDIT_ITEM: 'editItem',
Expand Down
70 changes: 63 additions & 7 deletions src/hooks/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
buildItemMembershipsKey,
buildItemParentsKey,
buildItemsKey,
buildPublicItemsWithTagKey,
buildS3FileContentKey,
OWN_ITEMS_KEY,
SHARED_ITEMS_KEY,
Expand Down Expand Up @@ -71,7 +72,12 @@ export default (

useChildren: (
id: UUID | undefined,
options?: { enabled?: boolean; ordered?: boolean; getUpdates?: boolean },
options?: {
enabled?: boolean;
ordered?: boolean;
getUpdates?: boolean;
placeholderData?: List<Item>;
},
) => {
const enabled = options?.enabled ?? true;
const ordered = options?.ordered ?? true;
Expand Down Expand Up @@ -100,6 +106,7 @@ export default (
},
...defaultOptions,
enabled: Boolean(id) && enabled,
placeholderData: options?.placeholderData,
});
},

Expand Down Expand Up @@ -152,7 +159,15 @@ export default (
});
},

useItem: (id?: UUID, options?: { getUpdates?: boolean }) => {
useItem: (
id?: UUID,
// todo: directly provide a Map<Item>
options?: {
getUpdates?: boolean;
placeholderData?: Item;
withMemberships?: boolean;
},
) => {
const getUpdates = options?.getUpdates ?? enableWebsocket;

itemWsHooks?.useItemUpdates(getUpdates ? id : null);
Expand All @@ -163,15 +178,25 @@ export default (
if (!id) {
throw new UndefinedArgument();
}
return Api.getItem(id, queryConfig).then((data) => Map(data));
return Api.getItem(
id,
{ withMemberships: options?.withMemberships },
queryConfig,
).then((data) => Map(data));
},
enabled: Boolean(id),
...defaultOptions,
placeholderData: options?.placeholderData
? Map(options?.placeholderData)
: undefined,
});
},

// todo: add optimisation to avoid fetching items already in cache
useItems: (ids: UUID[], options?: { getUpdates?: boolean }) => {
useItems: (
ids: UUID[],
options?: { getUpdates?: boolean; withMemberships?: boolean },
) => {
const getUpdates = options?.getUpdates ?? enableWebsocket;

ids.map((id) => itemWsHooks?.useItemUpdates(getUpdates ? id : null));
Expand All @@ -182,7 +207,11 @@ export default (
// eslint-disable-next-line no-nested-ternary
ids
? ids.length === 1
? Api.getItem(ids[0], queryConfig).then((data) => List([data]))
? Api.getItem(
ids[0],
{ withMemberships: options?.withMemberships ?? false },
queryConfig,
).then((data) => List([data]))
: Api.getItems(ids, queryConfig).then((data) => List(data))
: undefined,
onSuccess: async (items: List<Item>) => {
Expand Down Expand Up @@ -269,8 +298,9 @@ export default (
}),

useRecycledItems: (member?: Member) => {
const dd = member?.extra ?? useCurrentMember().data?.get('extra');
const itemId = dd?.recycleBin?.itemId;
const memberExtra =
member?.extra ?? useCurrentMember().data?.get('extra');
const itemId = memberExtra?.recycleBin?.itemId;
return useQuery({
queryKey: buildItemChildrenKey(itemId),
queryFn: () =>
Expand All @@ -286,5 +316,31 @@ export default (
...defaultOptions,
});
},

usePublicItemsWithTag: (
tagId: UUID,
{
withMemberships = false,
placeholderData,
}: { withMemberships?: boolean; placeholderData?: List<Item> },
) =>
useQuery({
queryKey: buildPublicItemsWithTagKey(tagId),
queryFn: () =>
Api.getPublicItemsWithTag(
{ tagId, withMemberships },
queryConfig,
).then((data) => List(data)),
onSuccess: async (items: List<Item>) => {
// save items in their own key
// eslint-disable-next-line no-unused-expressions
items?.forEach(async (item) => {
const { id } = item;
queryClient.setQueryData(buildItemKey(id), Map(item));
});
},
...defaultOptions,
placeholderData,
}),
};
};
Loading

0 comments on commit ec515e0

Please sign in to comment.