feat: gql codegen + caching + optimistic
This commit is contained in:
@@ -6,7 +6,6 @@ import {
|
||||
reactive,
|
||||
Ref,
|
||||
} from "@nuxtjs/composition-api"
|
||||
import { DocumentNode } from "graphql/language"
|
||||
import {
|
||||
createClient,
|
||||
TypedDocumentNode,
|
||||
@@ -28,6 +27,8 @@ import clone from "lodash/clone"
|
||||
import { keyDefs } from "./caching/keys"
|
||||
import { optimisticDefs } from "./caching/optimistic"
|
||||
import { updatesDef } from "./caching/updates"
|
||||
import { resolversDef } from "./caching/resolvers"
|
||||
import schema from "./backend-schema.json"
|
||||
import {
|
||||
getAuthIDToken,
|
||||
probableUser$,
|
||||
@@ -49,11 +50,12 @@ const client = createClient({
|
||||
exchanges: [
|
||||
devtoolsExchange,
|
||||
dedupExchange,
|
||||
// TODO: Extract this outttttttt
|
||||
offlineExchange({
|
||||
schema: schema as any,
|
||||
keys: keyDefs,
|
||||
optimistic: optimisticDefs,
|
||||
updates: updatesDef,
|
||||
resolvers: resolversDef,
|
||||
storage,
|
||||
}),
|
||||
authExchange({
|
||||
@@ -142,10 +144,10 @@ export function isLoadedGQLQuery<QueryFailType extends string, QueryReturnType>(
|
||||
|
||||
export function useGQLQuery<
|
||||
QueryReturnType = any,
|
||||
QueryFailType extends string = "",
|
||||
QueryVariables extends object = {}
|
||||
QueryVariables extends object = {},
|
||||
QueryFailType extends string = ""
|
||||
>(
|
||||
query: string | DocumentNode | TypedDocumentNode<any, QueryVariables>,
|
||||
query: TypedDocumentNode<QueryReturnType, QueryVariables>,
|
||||
variables?: QueryVariables,
|
||||
options: Partial<GQL_QUERY_OPTIONS> = DEFAULT_QUERY_OPTIONS
|
||||
):
|
||||
@@ -223,19 +225,19 @@ export function useGQLQuery<
|
||||
}
|
||||
|
||||
export const runMutation = <
|
||||
MutationReturnType = any,
|
||||
MutationFailType extends string = "",
|
||||
MutationVariables extends {} = {}
|
||||
DocType,
|
||||
DocVariables extends object | undefined,
|
||||
DocErrors extends string
|
||||
>(
|
||||
mutation: string | DocumentNode | TypedDocumentNode<any, MutationVariables>,
|
||||
variables?: MutationVariables,
|
||||
mutation: TypedDocumentNode<DocType, DocVariables>,
|
||||
variables?: DocVariables,
|
||||
additionalConfig?: Partial<OperationContext>
|
||||
): TE.TaskEither<GQLError<MutationFailType>, NonNullable<MutationReturnType>> =>
|
||||
): TE.TaskEither<GQLError<DocErrors>, DocType> =>
|
||||
pipe(
|
||||
TE.tryCatch(
|
||||
() =>
|
||||
client
|
||||
.mutation<MutationReturnType>(mutation, variables, {
|
||||
.mutation(mutation, variables, {
|
||||
requestPolicy: "cache-and-network",
|
||||
...additionalConfig,
|
||||
})
|
||||
@@ -244,7 +246,7 @@ export const runMutation = <
|
||||
),
|
||||
TE.chainEitherK((result) =>
|
||||
pipe(
|
||||
result.data as MutationReturnType,
|
||||
result.data,
|
||||
E.fromNullable(
|
||||
// Result is null
|
||||
pipe(
|
||||
@@ -253,13 +255,13 @@ export const runMutation = <
|
||||
E.match(
|
||||
// The left case (network error was null)
|
||||
(gqlErr) =>
|
||||
<GQLError<MutationFailType>>{
|
||||
<GQLError<DocErrors>>{
|
||||
type: "gql_error",
|
||||
error: gqlErr as MutationFailType,
|
||||
error: gqlErr,
|
||||
},
|
||||
// The right case (it was a GraphQL Error)
|
||||
(networkErr) =>
|
||||
<GQLError<MutationFailType>>{
|
||||
<GQLError<DocErrors>>{
|
||||
type: "network_error",
|
||||
error: networkErr,
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { KeyingConfig } from "@urql/exchange-graphcache";
|
||||
import { KeyingConfig } from "@urql/exchange-graphcache"
|
||||
|
||||
export const keyDefs: KeyingConfig = {
|
||||
User: (data) => (data as any).uid,
|
||||
TeamMember: (data) => (data as any).membershipID,
|
||||
Team: (data) => data.id as any,
|
||||
}
|
||||
}
|
||||
|
||||
14
packages/hoppscotch-app/helpers/backend/caching/resolvers.ts
Normal file
14
packages/hoppscotch-app/helpers/backend/caching/resolvers.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { ResolverConfig } from "@urql/exchange-graphcache"
|
||||
|
||||
export const resolversDef: ResolverConfig = {
|
||||
Query: {
|
||||
team: (_parent, { teamID }, _cache, _info) => ({
|
||||
__typename: "Team",
|
||||
id: teamID as any,
|
||||
}),
|
||||
user: (_parent, { uid }, _cache, _info) => ({
|
||||
__typename: "User",
|
||||
uid: uid as any,
|
||||
}),
|
||||
},
|
||||
}
|
||||
@@ -1,18 +1,12 @@
|
||||
import { UpdatesConfig } from "@urql/exchange-graphcache"
|
||||
import gql from "graphql-tag"
|
||||
import { MyTeamsDocument } from "../graphql"
|
||||
|
||||
export const updatesDef: Partial<UpdatesConfig> = {
|
||||
Mutation: {
|
||||
deleteTeam: (_r, { teamID }, cache, _info) => {
|
||||
cache.updateQuery(
|
||||
{
|
||||
query: gql`
|
||||
query {
|
||||
myTeams {
|
||||
id
|
||||
}
|
||||
}
|
||||
`,
|
||||
query: MyTeamsDocument,
|
||||
},
|
||||
(data: any) => {
|
||||
data.myTeams = (data as any).myTeams.filter(
|
||||
@@ -31,13 +25,7 @@ export const updatesDef: Partial<UpdatesConfig> = {
|
||||
leaveTeam: (_r, { teamID }, cache, _info) => {
|
||||
cache.updateQuery(
|
||||
{
|
||||
query: gql`
|
||||
query {
|
||||
myTeams {
|
||||
id
|
||||
}
|
||||
}
|
||||
`,
|
||||
query: MyTeamsDocument,
|
||||
},
|
||||
(data: any) => {
|
||||
data.myTeams = (data as any).myTeams.filter(
|
||||
@@ -56,13 +44,7 @@ export const updatesDef: Partial<UpdatesConfig> = {
|
||||
createTeam: (result, _args, cache, _info) => {
|
||||
cache.updateQuery(
|
||||
{
|
||||
query: gql`
|
||||
{
|
||||
myTeams {
|
||||
id
|
||||
}
|
||||
}
|
||||
`,
|
||||
query: MyTeamsDocument,
|
||||
},
|
||||
(data: any) => {
|
||||
data.myTeams.push(result.createTeam)
|
||||
@@ -70,77 +52,25 @@ export const updatesDef: Partial<UpdatesConfig> = {
|
||||
}
|
||||
)
|
||||
},
|
||||
renameTeam: (_result, { teamID, newName }, cache, _info) => {
|
||||
cache.updateQuery(
|
||||
{
|
||||
query: gql`
|
||||
query GetTeam($teamID: ID!) {
|
||||
team(teamID: $teamID) {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
teamID,
|
||||
removeTeamMember: (_result, { teamID, userUid }, cache) => {
|
||||
const newMembers = (
|
||||
cache.resolve(
|
||||
{
|
||||
__typename: "Team",
|
||||
id: teamID as string,
|
||||
},
|
||||
},
|
||||
(data: any) => {
|
||||
data.team.name = newName
|
||||
return data
|
||||
}
|
||||
"members"
|
||||
) as string[]
|
||||
)
|
||||
},
|
||||
removeTeamMember: (_result, { userUid, teamID }, cache) => {
|
||||
cache.updateQuery(
|
||||
{
|
||||
query: gql`
|
||||
query GetTeam($teamID: ID!) {
|
||||
team(teamID: $teamID) {
|
||||
members {
|
||||
user {
|
||||
uid
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
teamID,
|
||||
},
|
||||
},
|
||||
(data: any) => {
|
||||
data.team.members = data.team.members.filter(
|
||||
(x: any) => x.user.uid !== userUid
|
||||
)
|
||||
return data
|
||||
}
|
||||
)
|
||||
},
|
||||
updateTeamMemberRole: (result, { userUid, teamID }, cache) => {
|
||||
cache.updateQuery(
|
||||
{
|
||||
query: gql`
|
||||
query GetTeam($teamID: ID!) {
|
||||
team(teamID: $teamID) {
|
||||
members {
|
||||
user {
|
||||
uid
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: {
|
||||
teamID,
|
||||
},
|
||||
},
|
||||
(data: any) => {
|
||||
data.team.members = data.team.members.map((x: any) =>
|
||||
x.user.uid !== userUid ? x : result
|
||||
)
|
||||
return data
|
||||
}
|
||||
.map((x) => [x, cache.resolve(x, "user") as string])
|
||||
.map(([key, userKey]) => [key, cache.resolve(userKey, "uid") as string])
|
||||
.filter(([_key, uid]) => uid !== userUid)
|
||||
.map(([key]) => key)
|
||||
|
||||
cache.link(
|
||||
{ __typename: "Team", id: teamID as string },
|
||||
"members",
|
||||
newMembers
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
mutation CreateTeam($name: String!) {
|
||||
createTeam(name: $name) {
|
||||
id
|
||||
name
|
||||
members {
|
||||
membershipID
|
||||
role
|
||||
user {
|
||||
uid
|
||||
displayName
|
||||
email
|
||||
photoURL
|
||||
}
|
||||
}
|
||||
myRole
|
||||
ownersCount
|
||||
editorsCount
|
||||
viewersCount
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
mutation DeleteTeam($teamID: ID!) {
|
||||
deleteTeam(teamID: $teamID)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
mutation LeaveTeam($teamID: ID!) {
|
||||
leaveTeam(teamID: $teamID)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
mutation RemoveTeamMember($userUid: ID!, $teamID: ID!) {
|
||||
removeTeamMember(userUid: $userUid, teamID: $teamID)
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
mutation RenameTeam($newName: String!, $teamID: ID!) {
|
||||
renameTeam(newName: $newName, teamID: $teamID) {
|
||||
id
|
||||
name
|
||||
members {
|
||||
membershipID
|
||||
user {
|
||||
uid
|
||||
}
|
||||
role
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
mutation UpdateTeamMemberRole(
|
||||
$newRole: TeamMemberRole!,
|
||||
$userUid: ID!,
|
||||
$teamID: ID!
|
||||
) {
|
||||
updateTeamMemberRole(
|
||||
newRole: $newRole
|
||||
userUid: $userUid
|
||||
teamID: $teamID
|
||||
) {
|
||||
membershipID
|
||||
role
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
query GetTeam($teamID: ID!) {
|
||||
team(teamID: $teamID) {
|
||||
id
|
||||
name
|
||||
members {
|
||||
membershipID
|
||||
user {
|
||||
uid
|
||||
}
|
||||
role
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
query Me {
|
||||
me {
|
||||
uid
|
||||
displayName
|
||||
photoURL
|
||||
eaInvited
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
query MyTeams {
|
||||
myTeams {
|
||||
id
|
||||
name
|
||||
myRole
|
||||
ownersCount
|
||||
members {
|
||||
membershipID
|
||||
user {
|
||||
photoURL
|
||||
displayName
|
||||
email
|
||||
uid
|
||||
}
|
||||
role
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,28 @@
|
||||
import gql from "graphql-tag"
|
||||
import { pipe } from "fp-ts/function"
|
||||
import * as TE from "fp-ts/TaskEither"
|
||||
import { runMutation } from "../GQLClient"
|
||||
import { TeamName } from "../types/TeamName"
|
||||
import { TeamMemberRole } from "../types/TeamMemberRole"
|
||||
import {
|
||||
CreateTeamDocument,
|
||||
CreateTeamMutation,
|
||||
CreateTeamMutationVariables,
|
||||
DeleteTeamDocument,
|
||||
DeleteTeamMutation,
|
||||
DeleteTeamMutationVariables,
|
||||
LeaveTeamDocument,
|
||||
LeaveTeamMutation,
|
||||
LeaveTeamMutationVariables,
|
||||
RemoveTeamMemberDocument,
|
||||
RemoveTeamMemberMutation,
|
||||
RemoveTeamMemberMutationVariables,
|
||||
RenameTeamDocument,
|
||||
RenameTeamMutation,
|
||||
RenameTeamMutationVariables,
|
||||
TeamMemberRole,
|
||||
UpdateTeamMemberRoleDocument,
|
||||
UpdateTeamMemberRoleMutation,
|
||||
UpdateTeamMemberRoleMutationVariables,
|
||||
} from "../graphql"
|
||||
|
||||
type DeleteTeamErrors =
|
||||
| "team/not_required_role"
|
||||
@@ -11,7 +30,7 @@ type DeleteTeamErrors =
|
||||
| "team/member_not_found"
|
||||
| "ea/not_invite_or_admin"
|
||||
|
||||
type ExitTeamErrors =
|
||||
type LeaveTeamErrors =
|
||||
| "team/invalid_id"
|
||||
| "team/member_not_found"
|
||||
| "ea/not_invite_or_admin"
|
||||
@@ -36,48 +55,22 @@ type RemoveTeamMemberErrors =
|
||||
export const createTeam = (name: TeamName) =>
|
||||
pipe(
|
||||
runMutation<
|
||||
{
|
||||
createTeam: {
|
||||
id: string
|
||||
name: string
|
||||
members: Array<{ membershipID: string }>
|
||||
myRole: TeamMemberRole
|
||||
ownersCount: number
|
||||
editorsCount: number
|
||||
viewersCount: number
|
||||
}
|
||||
},
|
||||
CreateTeamMutation,
|
||||
CreateTeamMutationVariables,
|
||||
CreateTeamErrors
|
||||
>(
|
||||
gql`
|
||||
mutation CreateTeam($name: String!) {
|
||||
createTeam(name: $name) {
|
||||
id
|
||||
name
|
||||
members {
|
||||
membershipID
|
||||
}
|
||||
myRole
|
||||
ownersCount
|
||||
editorsCount
|
||||
viewersCount
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
name,
|
||||
}
|
||||
),
|
||||
>(CreateTeamDocument, {
|
||||
name,
|
||||
}),
|
||||
TE.map(({ createTeam }) => createTeam)
|
||||
)
|
||||
|
||||
export const deleteTeam = (teamID: string) =>
|
||||
runMutation<void, DeleteTeamErrors>(
|
||||
gql`
|
||||
mutation DeleteTeam($teamID: ID!) {
|
||||
deleteTeam(teamID: $teamID)
|
||||
}
|
||||
`,
|
||||
runMutation<
|
||||
DeleteTeamMutation,
|
||||
DeleteTeamMutationVariables,
|
||||
DeleteTeamErrors
|
||||
>(
|
||||
DeleteTeamDocument,
|
||||
{
|
||||
teamID,
|
||||
},
|
||||
@@ -87,12 +80,8 @@ export const deleteTeam = (teamID: string) =>
|
||||
)
|
||||
|
||||
export const leaveTeam = (teamID: string) =>
|
||||
runMutation<void, ExitTeamErrors>(
|
||||
gql`
|
||||
mutation ExitTeam($teamID: ID!) {
|
||||
leaveTeam(teamID: $teamID)
|
||||
}
|
||||
`,
|
||||
runMutation<LeaveTeamMutation, LeaveTeamMutationVariables, LeaveTeamErrors>(
|
||||
LeaveTeamDocument,
|
||||
{
|
||||
teamID,
|
||||
},
|
||||
@@ -104,27 +93,13 @@ export const leaveTeam = (teamID: string) =>
|
||||
export const renameTeam = (teamID: string, newName: TeamName) =>
|
||||
pipe(
|
||||
runMutation<
|
||||
{
|
||||
renameTeam: {
|
||||
id: string
|
||||
name: TeamName
|
||||
}
|
||||
},
|
||||
RenameTeamMutation,
|
||||
RenameTeamMutationVariables,
|
||||
RenameTeamErrors
|
||||
>(
|
||||
gql`
|
||||
mutation RenameTeam($newName: String!, $teamID: ID!) {
|
||||
renameTeam(newName: $newName, teamID: $teamID) {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
newName,
|
||||
teamID,
|
||||
}
|
||||
),
|
||||
>(RenameTeamDocument, {
|
||||
newName,
|
||||
teamID,
|
||||
}),
|
||||
TE.map(({ renameTeam }) => renameTeam)
|
||||
)
|
||||
|
||||
@@ -135,48 +110,23 @@ export const updateTeamMemberRole = (
|
||||
) =>
|
||||
pipe(
|
||||
runMutation<
|
||||
{
|
||||
updateTeamMemberRole: {
|
||||
membershipID: string
|
||||
role: TeamMemberRole
|
||||
}
|
||||
},
|
||||
UpdateTeamMemberRoleMutation,
|
||||
UpdateTeamMemberRoleMutationVariables,
|
||||
UpdateTeamMemberRoleErrors
|
||||
>(
|
||||
gql`
|
||||
mutation UpdateTeamMemberRole(
|
||||
$newRole: TeamMemberRole!,
|
||||
$userUid: ID!,
|
||||
teamID: ID!
|
||||
) {
|
||||
updateTeamMemberRole(
|
||||
newRole: $newRole
|
||||
userUid: $userUid
|
||||
teamID: $teamID
|
||||
) {
|
||||
membershipID
|
||||
role
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
newRole,
|
||||
userUid,
|
||||
teamID,
|
||||
}
|
||||
),
|
||||
>(UpdateTeamMemberRoleDocument, {
|
||||
newRole,
|
||||
userUid,
|
||||
teamID,
|
||||
}),
|
||||
TE.map(({ updateTeamMemberRole }) => updateTeamMemberRole)
|
||||
)
|
||||
|
||||
export const removeTeamMember = (userUid: string, teamID: string) =>
|
||||
runMutation<void, RemoveTeamMemberErrors>(
|
||||
gql`
|
||||
mutation RemoveTeamMember($userUid: ID!, $teamID: ID!) {
|
||||
removeTeamMember(userUid: $userUid, teamID: $teamID)
|
||||
}
|
||||
`,
|
||||
{
|
||||
userUid,
|
||||
teamID,
|
||||
}
|
||||
)
|
||||
runMutation<
|
||||
RemoveTeamMemberMutation,
|
||||
RemoveTeamMemberMutationVariables,
|
||||
RemoveTeamMemberErrors
|
||||
>(RemoveTeamMemberDocument, {
|
||||
userUid,
|
||||
teamID,
|
||||
})
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export type TeamMemberRole = "OWNER" | "EDITOR" | "VIEWER"
|
||||
Reference in New Issue
Block a user