[next] User Mutations (#2133)

* feat: added support for Token's

* feat: added mutations

- updateUserUsername
- updateUserDisplayName
- updateUserAvatar
- updateUserEmail
- updateUserRole

* lint: removed old commented out code

* fix: linting
This commit is contained in:
Wyatt Johnson
2018-12-20 22:42:44 +00:00
committed by GitHub
parent 01a2ace3f0
commit 98a8c8dc71
10 changed files with 964 additions and 111 deletions
+6 -1
View File
@@ -5,6 +5,7 @@ import CommonContext from "talk-server/graph/common/context";
import { Tenant } from "talk-server/models/tenant";
import { User } from "talk-server/models/user";
import { TaskQueue } from "talk-server/queue";
import { JWTSigningConfig } from "talk-server/services/jwt";
import { AugmentedRedis } from "talk-server/services/redis";
import TenantCache from "talk-server/services/tenant/cache";
import { Request } from "talk-server/types/express";
@@ -19,6 +20,7 @@ export interface TenantContextOptions {
tenantCache: TenantCache;
queue: TaskQueue;
config: Config;
signingConfig?: JWTSigningConfig;
req?: Request;
user?: User;
}
@@ -26,12 +28,13 @@ export interface TenantContextOptions {
export default class TenantContext extends CommonContext {
public readonly tenant: Tenant;
public readonly tenantCache: TenantCache;
public readonly user?: User;
public readonly mongo: Db;
public readonly redis: AugmentedRedis;
public readonly queue: TaskQueue;
public readonly loaders: ReturnType<typeof loaders>;
public readonly mutators: ReturnType<typeof mutators>;
public readonly user?: User;
public readonly signingConfig?: JWTSigningConfig;
constructor({
req,
@@ -42,6 +45,7 @@ export default class TenantContext extends CommonContext {
config,
tenantCache,
queue,
signingConfig,
}: TenantContextOptions) {
super({ user, req, config });
@@ -51,6 +55,7 @@ export default class TenantContext extends CommonContext {
this.mongo = mongo;
this.redis = redis;
this.queue = queue;
this.signingConfig = signingConfig;
this.loaders = loaders(this);
this.mutators = mutators(this);
}
@@ -1,5 +1,4 @@
import { GraphQLSchema } from "graphql";
// import { graphqlBatchHTTPWrapper } from "react-relay-network-layer";
import { graphqlBatchMiddleware } from "talk-server/app/middleware/graphqlBatch";
import { Config } from "talk-server/config";
@@ -1,16 +1,30 @@
import TenantContext from "talk-server/graph/tenant/context";
import * as user from "talk-server/models/user";
import {
createToken,
deactivateToken,
setEmail,
setPassword,
setUsername,
updateAvatar,
updateDisplayName,
updateEmail,
updatePassword,
updateRole,
updateUsername,
} from "talk-server/services/users";
import {
GQLCreateTokenInput,
GQLDeactivateTokenInput,
GQLSetEmailInput,
GQLSetPasswordInput,
GQLSetUsernameInput,
GQLUpdatePasswordInput,
GQLUpdateUserAvatarInput,
GQLUpdateUserDisplayNameInput,
GQLUpdateUserEmailInput,
GQLUpdateUserRoleInput,
GQLUpdateUserUsernameInput,
} from "../schema/__generated__/types";
export const User = (ctx: TenantContext) => ({
@@ -30,4 +44,25 @@ export const User = (ctx: TenantContext) => ({
input: GQLUpdatePasswordInput
): Promise<Readonly<user.User> | null> =>
updatePassword(ctx.mongo, ctx.tenant, ctx.user!, input.password),
createToken: async (input: GQLCreateTokenInput) =>
createToken(
ctx.mongo,
ctx.tenant,
// NOTE: (wyattjoh) this will error if not provided.
ctx.signingConfig!,
ctx.user!,
input.name
),
deactivateToken: async (input: GQLDeactivateTokenInput) =>
deactivateToken(ctx.mongo, ctx.tenant, ctx.user!, input.id),
updateUserUsername: async (input: GQLUpdateUserUsernameInput) =>
updateUsername(ctx.mongo, ctx.tenant, input.userID, input.username),
updateUserDisplayName: async (input: GQLUpdateUserDisplayNameInput) =>
updateDisplayName(ctx.mongo, ctx.tenant, input.userID, input.displayName),
updateUserEmail: async (input: GQLUpdateUserEmailInput) =>
updateEmail(ctx.mongo, ctx.tenant, input.userID, input.email),
updateUserAvatar: async (input: GQLUpdateUserAvatarInput) =>
updateAvatar(ctx.mongo, ctx.tenant, input.userID, input.avatar),
updateUserRole: async (input: GQLUpdateUserRoleInput) =>
updateRole(ctx.mongo, ctx.tenant, input.userID, input.role),
});
@@ -97,4 +97,32 @@ export const Mutation: Required<GQLMutationTypeResolver<void>> = {
user: await ctx.mutators.User.updatePassword(input),
clientMutationId: input.clientMutationId,
}),
createToken: async (source, { input }, ctx) => ({
...(await ctx.mutators.User.createToken(input)),
clientMutationId: input.clientMutationId,
}),
deactivateToken: async (source, { input }, ctx) => ({
...(await ctx.mutators.User.deactivateToken(input)),
clientMutationId: input.clientMutationId,
}),
updateUserUsername: async (source, { input }, ctx) => ({
user: await ctx.mutators.User.updateUserUsername(input),
clientMutationId: input.clientMutationId,
}),
updateUserDisplayName: async (source, { input }, ctx) => ({
user: await ctx.mutators.User.updateUserDisplayName(input),
clientMutationId: input.clientMutationId,
}),
updateUserEmail: async (source, { input }, ctx) => ({
user: await ctx.mutators.User.updateUserEmail(input),
clientMutationId: input.clientMutationId,
}),
updateUserAvatar: async (source, { input }, ctx) => ({
user: await ctx.mutators.User.updateUserAvatar(input),
clientMutationId: input.clientMutationId,
}),
updateUserRole: async (source, { input }, ctx) => ({
user: await ctx.mutators.User.updateUserRole(input),
clientMutationId: input.clientMutationId,
}),
};
@@ -984,41 +984,6 @@ enum USER_ROLE {
ADMIN
}
enum USER_USERNAME_STATUS {
"""
UNSET is used when the username can be changed, and does not necessarily
require moderator action to become active. This can be used when the user
signs up with a social login and has the option of setting their own
username.
"""
UNSET
"""
SET is used when the username has been set for the first time, but cannot
change without the username being rejected by a moderator and that moderator
agreeing that the username should be allowed to change.
"""
SET
"""
APPROVED is used when the username was changed, and subsequently approved by
said moderator.
"""
APPROVED
"""
REJECTED is used when the username was changed, and subsequently rejected by
said moderator.
"""
REJECTED
"""
CHANGED is used after a user has changed their username after it was
rejected.
"""
CHANGED
}
type LocalProfile {
id: String!
}
@@ -1047,6 +1012,12 @@ union Profile =
| FacebookProfile
| GoogleProfile
type Token {
id: ID!
name: String!
createdAt: Time!
}
"""
User is someone that leaves Comments, and logs in.
"""
@@ -1108,6 +1079,11 @@ type User {
first: Int = 10
after: Cursor
): CommentModerationActionConnection! @auth(role: [MODERATOR, ADMIN])
"""
tokens lists the access tokens associated with the account.
"""
tokens: [Token!]! @auth(role: [ADMIN], userIDField: "id")
}
################################################################################
@@ -2943,6 +2919,244 @@ type UpdatePasswordPayload {
clientMutationId: String!
}
##################
# createToken
##################
input CreateTokenInput {
"""
name is the desired name for the Token.
"""
name: String!
"""
clientMutationId is required for Relay support.
"""
clientMutationId: String!
}
type CreateTokenPayload {
"""
user is the possibly modified User.
"""
user: User!
"""
token is the Token that was created.
"""
token: Token!
"""
signedToken is the signed Token associated with the account.
"""
signedToken: String!
"""
clientMutationId is required for Relay support.
"""
clientMutationId: String!
}
##################
# deactivateToken
##################
input DeactivateTokenInput {
"""
id is the ID of the Token that should be deactivated.
"""
id: ID!
"""
clientMutationId is required for Relay support.
"""
clientMutationId: String!
}
type DeactivateTokenPayload {
"""
user is the possibly modified User.
"""
user: User!
"""
token is the Token that was deleted.
"""
token: Token
"""
clientMutationId is required for Relay support.
"""
clientMutationId: String!
}
##################
# updateUserUsername
##################
input UpdateUserUsernameInput {
"""
userID is the ID of the User that should have their username updated.
"""
userID: ID!
"""
username is the desired username to set for the User.
"""
username: String!
"""
clientMutationId is required for Relay support.
"""
clientMutationId: String!
}
type UpdateUserUsernamePayload {
"""
user is the possibly modified User.
"""
user: User!
"""
clientMutationId is required for Relay support.
"""
clientMutationId: String!
}
##################
# updateUserDisplayName
##################
input UpdateUserDisplayNameInput {
"""
userID is the ID of the User that should have their display name updated.
"""
userID: ID!
"""
displayName is the desired display name to set for the User. If set to `null`
or not provided, it will be unset.
"""
displayName: String
"""
clientMutationId is required for Relay support.
"""
clientMutationId: String!
}
type UpdateUserDisplayNamePayload {
"""
user is the possibly modified User.
"""
user: User!
"""
clientMutationId is required for Relay support.
"""
clientMutationId: String!
}
##################
# updateUserEmail
##################
input UpdateUserEmailInput {
"""
userID is the ID of the User that should have their email address updated.
"""
userID: ID!
"""
email is the email address to set for the User.
"""
email: String!
"""
clientMutationId is required for Relay support.
"""
clientMutationId: String!
}
type UpdateUserEmailPayload {
"""
user is the possibly modified User.
"""
user: User!
"""
clientMutationId is required for Relay support.
"""
clientMutationId: String!
}
##################
# updateUserAvatar
##################
input UpdateUserAvatarInput {
"""
userID is the ID of the User that should have their avatar updated.
"""
userID: ID!
"""
avatar is the URL to the avatar to set for the User. If set to `null` or not
provided, it will be unset.
"""
avatar: String
"""
clientMutationId is required for Relay support.
"""
clientMutationId: String!
}
type UpdateUserAvatarPayload {
"""
user is the possibly modified User.
"""
user: User!
"""
clientMutationId is required for Relay support.
"""
clientMutationId: String!
}
##################
# updateUserRole
##################
input UpdateUserRoleInput {
"""
userID is the ID of the User that should have their avatar updated.
"""
userID: ID!
"""
role is the `USER_ROLE` that the User should be set to.
"""
role: USER_ROLE!
"""
clientMutationId is required for Relay support.
"""
clientMutationId: String!
}
type UpdateUserRolePayload {
"""
user is the possibly modified User.
"""
user: User!
"""
clientMutationId is required for Relay support.
"""
clientMutationId: String!
}
##################
## Mutation
##################
@@ -3085,12 +3299,54 @@ type Mutation {
they already have one associated with them.
"""
updatePassword(input: UpdatePasswordInput!): UpdatePasswordPayload @auth
}
################################################################################
## Subscriptions
################################################################################
"""
createToken allows an administrator to create a Token based on the current
logged in User.
"""
createToken(input: CreateTokenInput!): CreateTokenPayload!
@auth(roles: [ADMIN])
type Subscription {
commentCreated(storyID: ID!): Comment
"""
deactivateToken will deactivate the current logged in User's Token based on
the input.
"""
deactivateToken(input: DeactivateTokenInput!): DeactivateTokenPayload!
@auth(roles: [ADMIN])
"""
updateUserUsername allows administrators to update a given User's username to
the one provided.
"""
updateUserUsername(
input: UpdateUserUsernameInput!
): UpdateUserUsernamePayload! @auth(roles: [ADMIN])
"""
updateUserDisplayName allows administrators to update a given User's display
name to the one provided.
"""
updateUserDisplayName(
input: UpdateUserDisplayNameInput!
): UpdateUserDisplayNamePayload @auth(roles: [ADMIN])
"""
updateUserEmail allows administrators to update a given User's email address
to the one provided.
"""
updateUserEmail(input: UpdateUserEmailInput!): UpdateUserEmailPayload!
@auth(roles: [ADMIN])
"""
updateUserAvatar allows administrators to update a given User's avatar to the
one provided.
"""
updateUserAvatar(input: UpdateUserAvatarInput!): UpdateUserAvatarPayload!
@auth(roles: [ADMIN])
"""
updateUserRole will update a given User's role.
"""
updateUserRole(input: UpdateUserRoleInput!): UpdateUserRolePayload!
@auth(roles: [ADMIN])
}