Skip to content

Commit

Permalink
feat(graphql-api): ability to extend connection information (#988)
Browse files Browse the repository at this point in the history
  • Loading branch information
pradel committed May 25, 2020
1 parent 956b2c6 commit 853afaa
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ describe('accounts-password resolvers mutation', () => {
};
let injector: { get: jest.Mock };
const user = { id: 'idTest' };
const ip = 'ipTest';
const userAgent = 'userAgentTest';
const infos = {
ip: 'ipTest',
userAgent: 'userAgentTest',
};

beforeEach(() => {
injector = {
Expand Down Expand Up @@ -188,16 +190,13 @@ describe('accounts-password resolvers mutation', () => {
const response = await Mutation.createUser!(
{},
{ user: createUserInput },
{ injector } as any,
{ injector, infos } as any,
{} as any
);
expect(injector.get).toHaveBeenCalledWith(AccountsPassword);
expect(injector.get).toHaveBeenCalledWith(AccountsServer);
expect(accountsPasswordLocalMock.createUser).toHaveBeenCalledWith(createUserInput);
expect(accountsServerLocalMock.loginWithUser).toHaveBeenCalledWith(
createdUser,
expect.any(Object)
);
expect(accountsServerLocalMock.loginWithUser).toHaveBeenCalledWith(createdUser, infos);
expect(response?.loginResult).not.toBeNull();
expect(response?.loginResult!.user).toEqual(createdUser);
expect(response?.loginResult!.tokens?.accessToken).toEqual('accessToken');
Expand Down Expand Up @@ -251,14 +250,11 @@ describe('accounts-password resolvers mutation', () => {
await Mutation.resetPassword!(
{},
{ token, newPassword } as any,
{ injector, ip, userAgent } as any,
{ injector, infos } as any,
{} as any
);
expect(injector.get).toHaveBeenCalledWith(AccountsPassword);
expect(accountsPasswordMock.resetPassword).toHaveBeenCalledWith(token, newPassword, {
ip,
userAgent,
});
expect(accountsPasswordMock.resetPassword).toHaveBeenCalledWith(token, newPassword, infos);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ describe('accounts resolvers mutation', () => {
get: jest.fn(() => accountsServerMock),
};
const authToken = 'authTokenTest';
const ip = 'ipTest';
const userAgent = 'userAgentTest';
const infos = {
ip: 'ipTest',
userAgent: 'userAgentTest',
};

beforeEach(() => {
jest.clearAllMocks();
Expand All @@ -29,28 +31,26 @@ describe('accounts resolvers mutation', () => {
await Mutation.authenticate!(
{},
{ serviceName, params } as any,
{ injector, ip, userAgent } as any,
{ injector, infos } as any,
{} as any
);
expect(injector.get).toHaveBeenCalledWith(AccountsServer);
expect(accountsServerMock.loginWithService).toHaveBeenCalledWith(serviceName, params, {
ip,
userAgent,
});
expect(accountsServerMock.loginWithService).toHaveBeenCalledWith(serviceName, params, infos);
});

it('should call authenticateWithService', async () => {
await Mutation.verifyAuthentication!(
{},
{ serviceName, params } as any,
{ injector, ip, userAgent } as any,
{ injector, infos } as any,
{} as any
);
expect(injector.get).toHaveBeenCalledWith(AccountsServer);
expect(accountsServerMock.authenticateWithService).toHaveBeenCalledWith(serviceName, params, {
ip,
userAgent,
});
expect(accountsServerMock.authenticateWithService).toHaveBeenCalledWith(
serviceName,
params,
infos
);
});
});

Expand All @@ -62,15 +62,11 @@ describe('accounts resolvers mutation', () => {
await Mutation.impersonate!(
{},
{ accessToken, username } as any,
{ injector, ip, userAgent } as any,
{ injector, infos } as any,
{} as any
);
expect(injector.get).toHaveBeenCalledWith(AccountsServer);
expect(accountsServerMock.impersonate).toHaveBeenCalledWith(
accessToken,
{ username },
{ ip, userAgent }
);
expect(accountsServerMock.impersonate).toHaveBeenCalledWith(accessToken, { username }, infos);
});
});

Expand All @@ -96,14 +92,15 @@ describe('accounts resolvers mutation', () => {
await Mutation.refreshTokens!(
{},
{ accessToken, refreshToken },
{ injector, ip, userAgent } as any,
{ injector, infos } as any,
{} as any
);
expect(injector.get).toHaveBeenCalledWith(AccountsServer);
expect(accountsServerMock.refreshTokens).toHaveBeenCalledWith(accessToken, refreshToken, {
ip,
userAgent,
});
expect(accountsServerMock.refreshTokens).toHaveBeenCalledWith(
accessToken,
refreshToken,
infos
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const Mutation: MutationResolvers<ModuleContext<AccountsModuleContext>> =
return null;
},
createUser: async (_, { user }, ctx) => {
const { ip, userAgent, injector } = ctx;
const { injector, infos } = ctx;
const accountsServer = injector.get(AccountsServer);
const accountsPassword = injector.get(AccountsPassword);

Expand Down Expand Up @@ -60,10 +60,7 @@ export const Mutation: MutationResolvers<ModuleContext<AccountsModuleContext>> =

// If we are here - user must be created successfully
// Explicitly saying this to Typescript compiler
const loginResult = await accountsServer.loginWithUser(createdUser!, {
ip,
userAgent,
});
const loginResult = await accountsServer.loginWithUser(createdUser!, infos);

return {
userId,
Expand Down Expand Up @@ -92,8 +89,8 @@ export const Mutation: MutationResolvers<ModuleContext<AccountsModuleContext>> =
await injector.get(AccountsPassword).twoFactor.unset(userId, code);
return null;
},
resetPassword: async (_, { token, newPassword }, { injector, ip, userAgent }) => {
return injector.get(AccountsPassword).resetPassword(token, newPassword, { ip, userAgent });
resetPassword: async (_, { token, newPassword }, { injector, infos }) => {
return injector.get(AccountsPassword).resetPassword(token, newPassword, infos);
},
sendResetPasswordEmail: async (_, { email }, { injector }) => {
await injector.get(AccountsPassword).sendResetPasswordEmail(email);
Expand Down
11 changes: 6 additions & 5 deletions packages/graphql-api/src/modules/accounts/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { GraphQLModule } from '@graphql-modules/core';
import { mergeTypeDefs } from '@graphql-toolkit/schema-merging';
import { User, ConnectionInformations } from '@accounts/types';
import { AccountsServer } from '@accounts/server';
import AccountsPassword from '@accounts/password';
import { IncomingMessage } from 'http';
import TypesTypeDefs from './schema/types';
import getQueryTypeDefs from './schema/query';
Expand All @@ -8,12 +11,9 @@ import getSchemaDef from './schema/schema-def';
import { Query } from './resolvers/query';
import { Mutation } from './resolvers/mutation';
import { User as UserResolvers } from './resolvers/user';
import { User } from '@accounts/types';
import { AccountsPasswordModule } from '../accounts-password';
import { AuthenticatedDirective } from '../../utils/authenticated-directive';
import { context } from '../../utils';
import AccountsPassword from '@accounts/password';
import { mergeTypeDefs } from '@graphql-toolkit/schema-merging';
import { CoreAccountsModule } from '../core';

export interface AccountsRequest {
Expand All @@ -33,10 +33,11 @@ export interface AccountsModuleConfig {

export interface AccountsModuleContext<IUser = User> {
authToken?: string;
userAgent: string | null;
ip: string | null;
user?: IUser;
userId?: string;
userAgent: string | null;
ip: string | null;
infos: ConnectionInformations;
}

// You can see the below. It is really easy to create a reusable GraphQL-Module with different configurations
Expand Down
24 changes: 10 additions & 14 deletions packages/graphql-api/src/modules/accounts/resolvers/mutation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,29 @@ import { AccountsServer } from '@accounts/server';
export const Mutation: MutationResolvers<ModuleContext<AccountsModuleContext>> = {
authenticate: async (_, args, ctx) => {
const { serviceName, params } = args;
const { ip, userAgent, injector } = ctx;
const { injector, infos } = ctx;

const authenticated = await injector.get(AccountsServer).loginWithService(serviceName, params, {
ip,
userAgent,
});
const authenticated = await injector
.get(AccountsServer)
.loginWithService(serviceName, params, infos);
return authenticated;
},
verifyAuthentication: async (_, args, ctx) => {
const { serviceName, params } = args;
const { ip, userAgent, injector } = ctx;
const { injector, infos } = ctx;

const authenticated = await injector
.get(AccountsServer)
.authenticateWithService(serviceName, params, {
ip,
userAgent,
});
.authenticateWithService(serviceName, params, infos);
return authenticated;
},
impersonate: async (_, args, ctx) => {
const { accessToken, username } = args;
const { ip, userAgent, injector } = ctx;
const { injector, infos } = ctx;

const impersonateRes = await injector
.get(AccountsServer)
.impersonate(accessToken, { username }, { ip, userAgent });
.impersonate(accessToken, { username }, infos);

// So ctx.user can be used in subsequent queries / mutations
if (impersonateRes && impersonateRes.user && impersonateRes.tokens) {
Expand All @@ -53,11 +49,11 @@ export const Mutation: MutationResolvers<ModuleContext<AccountsModuleContext>> =
},
refreshTokens: async (_, args, ctx) => {
const { accessToken, refreshToken } = args;
const { ip, userAgent, injector } = ctx;
const { injector, infos } = ctx;

const refreshedSession = await injector
.get(AccountsServer)
.refreshTokens(accessToken, refreshToken, { ip, userAgent });
.refreshTokens(accessToken, refreshToken, infos);

return refreshedSession;
},
Expand Down
15 changes: 12 additions & 3 deletions packages/graphql-api/src/utils/context-builder.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AccountsRequest, AccountsModuleConfig } from '../modules';
import { ModuleConfig, ModuleSessionInfo } from '@graphql-modules/core';
import { getClientIp } from 'request-ip';
import { AccountsRequest, AccountsModuleConfig } from '../modules';

export const context = (moduleName: string) => async (
{ req }: AccountsRequest,
Expand All @@ -11,6 +11,10 @@ export const context = (moduleName: string) => async (
return {
ip: '',
userAgent: '',
infos: {
ip: '',
userAgent: '',
},
};
}

Expand All @@ -28,6 +32,7 @@ export const context = (moduleName: string) => async (
}
}

const ip = getClientIp(req);
let userAgent: string = (req.headers['user-agent'] as string) || '';
if (req.headers['x-ucbrowser-ua']) {
// special case of UC Browser
Expand All @@ -36,9 +41,13 @@ export const context = (moduleName: string) => async (

return {
authToken,
userAgent,
ip: getClientIp(req),
user,
userId: user && user.id,
userAgent,
ip,
infos: {
userAgent,
ip,
},
};
};
1 change: 1 addition & 0 deletions packages/types/src/types/connection-informations.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface ConnectionInformations {
ip?: string | null;
userAgent?: string | null;
[key: string]: any;
}

0 comments on commit 853afaa

Please sign in to comment.