From 955e2e21c436a16246a855abc5caf7a05303701b Mon Sep 17 00:00:00 2001 From: Barry Low Date: Fri, 2 Nov 2018 01:33:13 -0700 Subject: [PATCH] Server: Fix error handling --- api/src/ApolloServer.js | 10 +- api/src/directives/index.js | 11 +- api/src/errors/index.js | 12 +- api/src/graphql/resolvers/Mutation/index.js | 130 +++++++++----------- api/src/graphql/resolvers/Query/Member.js | 5 +- api/src/logger/errors.js | 7 +- api/src/logger/index.js | 6 +- client/package.json | 2 +- 8 files changed, 93 insertions(+), 90 deletions(-) diff --git a/api/src/ApolloServer.js b/api/src/ApolloServer.js index 6130a0432..c0eb6da35 100644 --- a/api/src/ApolloServer.js +++ b/api/src/ApolloServer.js @@ -75,12 +75,12 @@ export default function createApolloServer( subscriptions: { path: subscriptionsEndpoint, onConnect: async (connection, websocket) => { - const { authorization } = connection + const { authorization, userid } = connection let contextData = {} try { // Simulate `req` object for auth - const req = { headers: { authorization } } + const req = { headers: { authorization, userid } } // Call all middlewares in order and modify `req` await new Promise((resolve, reject) => @@ -97,11 +97,7 @@ export default function createApolloServer( req, }) } catch (err) { - if (err.status !== 401) { - logger.error(err) - } - - throw err + logger.error(err) } return contextData diff --git a/api/src/directives/index.js b/api/src/directives/index.js index 03f2ae250..4d5da93a0 100644 --- a/api/src/directives/index.js +++ b/api/src/directives/index.js @@ -76,15 +76,18 @@ export const directiveResolvers = { }, isOwner: async (next, source, { type }, ctx) => { logger.info('directive isOwner: ', source, type, ctx) - const { slug: typeId } = - source && source.id - ? source - : ctx.request.body.variables ? ctx.request.body.variables : { id: null } + logger.info(ctx.request.body.variables) + logger.info(type) + const { _v0_slug: typeId } = ctx.request.body.variables ? ctx.request.body.variables : { _v0_slug: null } + logger.info('typeId') + logger.info(typeId) const userId = await isUserId(ctx) const isOwner = type === `Member` ? memberId === typeId : await isRequestingMemberAlsoOwner({ ctx, userId, type, typeId }) + logger.info('isOwner') + logger.info(isOwner) if (isOwner) { return next() } diff --git a/api/src/errors/index.js b/api/src/errors/index.js index d42bf2db7..86347d856 100644 --- a/api/src/errors/index.js +++ b/api/src/errors/index.js @@ -16,17 +16,25 @@ export const formatError = (error) => { logger.info('formatError') logger.error(JSON.stringify(error)) const originalError = searchOriginalError(error) + logger.error('originalError') logger.error(originalError) - return originalError + const { message, code } = originalError + return { message, code} } const searchOriginalError = (error) => { if (error.originalError) { - return searchOriginalError(error.originalError) + logger.error('Original Error') + logger.error(JSON.stringify(error.originalError)) + return error.originalError } if (error.errors) { + logger.error('Errors') + logger.error(JSON.stringify(error.errors)) return error.errors.map(searchOriginalError)[0] } + logger.error('searchOriginalErrors') + logger.error(JSON.stringify(error)) return error; } diff --git a/api/src/graphql/resolvers/Mutation/index.js b/api/src/graphql/resolvers/Mutation/index.js index dd9605583..1057f5528 100644 --- a/api/src/graphql/resolvers/Mutation/index.js +++ b/api/src/graphql/resolvers/Mutation/index.js @@ -1,4 +1,5 @@ import axios from 'axios' +import { ApolloError } from 'apollo-server-express' import { getUser } from '../../../middleware/getMember' @@ -8,7 +9,7 @@ import TEAMS_CONNECTIONS_FRAGMENT from '../../fragments/TeamsConnectionsFragment import TEAMS_FRAGMENT from '../../fragments/TeamsFragment' import TEAM_FB_FRAGMENT from '../../fragments/TeamFBFragment' -import { logger, AuthenticationError, ServiceUnavailableError, DatabaseError } from '../../../logger' +import { logger, AuthenticationError, ServiceUnavailableError, DatabaseError, ConflictError } from '../../../logger' export const resolvers = { Mutation: { @@ -43,85 +44,75 @@ export const resolvers = { async createTeam(parent, { name, slug, botName, botSlug }, ctx, info) { logger.info('createTeam ctx.request.user:') logger.info(ctx.request.headers.userid) - const authId = ctx.request.headers.userid if (!authId) { throw new AuthenticationError() } + const userData = await getUser(authId) + logger.info('userData:') + logger.info(JSON.stringify(await userData)) + const alias = await userData.data.users_User.alias + const email = await userData.data.users_User.email + const avatar = 'https://aadevelop.blob.core.windows.net/module-teams/module-default/aa-avatar-default.png' + const banner = 'https://aadevelop.blob.core.windows.net/module-teams/module-default/aa-banner-default.png' - return getUser(authId) - .then(async result => { - logger.info('axios:') - logger.info(result) + const createTeamUrl = encodeURI(`${slug}/${name}/${alias}/${botSlug}/${botName}`) + logger.info('createTeamUrl:') + logger.info(JSON.stringify(await createTeamUrl)) - const alias = result.data.users_User.alias - const email = result.data.users_User.email - const avatar = 'https://aadevelop.blob.core.windows.net/module-teams/module-default/aa-avatar-default.png' - const banner = 'https://aadevelop.blob.core.windows.net/module-teams/module-default/aa-banner-default.png' + const platformUrl = 'https://develop.advancedalgos.net/AABrowserAPI/newTeam/' + // const platformUrl = 'http://localhost:1337/AABrowserAPI/teamSetup/' - const createTeamUrl = encodeURI(`${slug}/${name}/${alias}/${botSlug}/${botName}`) - logger.info('createTeamUrl:') - logger.info(JSON.stringify(await createTeamUrl)) - - const platformUrl = 'https://develop.advancedalgos.net/AABrowserAPI/newTeam/' - // const platformUrl = 'http://localhost:1337/AABrowserAPI/teamSetup/' - - logger.info(`${platformUrl}${createTeamUrl}/${authId}`) - const createPlatformTeam = await axios.get(`${platformUrl}${createTeamUrl}/${authId}`) - .then((result) => { - console.log('createPlatformTeam result:', result.data) - if(result.data.message === 'Team Name already taken'){ - throw new DatabaseError(`${result.data.message} - Team already exists onAA Cloud`) - } - if(result.data.result === 'Fail'){ - throw new DatabaseError(`${result.data.message} - Creating team on AA Cloud has failed`) - } - return result - }) - .catch(err =>{ - logger.debug('createPlatformTeam err:') - logger.debug(err) - throw new DatabaseError(err) - }) - console.log('createPlatformTeam returned: ', await createPlatformTeam) - if(await createPlatformTeam === 'Team Name already taken'){ - return createPlatformTeam + logger.info(`${platformUrl}${createTeamUrl}/${authId}`) + const createPlatformTeam = await axios.get(`${platformUrl}${createTeamUrl}/${authId}`) + .then((result) => { + console.log('createPlatformTeam result:', result.data) + if(result.data.message === 'Team Name already taken'){ + return { error: 'Team already exist on AA Cloud.', code: 409 } } - - const existingMember = await ctx.db.query.member({ where: { authId } }, `{id}`) - .catch((err) => { - logger.debug(err, 'existingMember error: ') - throw new DatabaseError(err) - }) - - logger.debug('existingMember:') - logger.debug(await existingMember) - - let createTeam - if(existingMember !== null && existingMember.id !== null){ - logger.info('createTeam with existingMember:') - createTeam = await ctx.db.mutation.createTeam({ data: {name: name, slug: slug, owner: authId, members: {create: {member: {connect: {authId: authId}}, role: 'OWNER', status: { create: { status: 'ACTIVE', reason: `Connected to Team ${name}`}}}}, profile: {create: {avatar: avatar, banner: banner}}, fb: {create: {name: botName, slug: botSlug, kind:'TRADER', avatar: avatar, status: {create: {status: 'ACTIVE', reason: "Cloned on team creation"}}}}, status: {create: {status: 'ACTIVE', reason:"Team created"}}} }, TEAMS_FRAGMENT) - .catch((err) => { - logger.debug(err, 'createTeamMutation error: ') - throw new DatabaseError(err) - }) - } else { - logger.info('createTeam without existingMember:') - createTeam = await ctx.db.mutation.createTeam({ data: {name: name, slug: slug, owner: authId, members: {create: {member: {create: {authId: authId, alias: alias, visible:'true', status: { create: { status: 'ACTIVE', reason: `Created team ${name}`}}}}, role: 'OWNER', status: { create: { status: 'ACTIVE', reason: `Created with Team ${name}`}}}}, profile: {create: {avatar: avatar, banner: banner}}, fb: {create: {name: botName, slug: botSlug, kind:'TRADER', avatar: avatar, status: {create: {status: 'ACTIVE', reason: "Cloned on team creation"}}}}, status: {create: {status: 'ACTIVE', reason:"Team created"}}} }, TEAMS_FRAGMENT) - .catch((err) => { - logger.debug(err, 'createTeamMutation error: ') - throw new DatabaseError(err) - }) + if(result.data.result === 'Fail'){ + return { error: `${result.data.message} - Creating team on AA Cloud has failed`, code: 404 } } - - // sendTeamCreateConfirmation(email, name, botName) - - return createTeam + return result }) + + logger.info('createPlatformTeam') + logger.info(JSON.stringify(await createPlatformTeam)) + logger.error(await createPlatformTeam.error) + logger.error(await createPlatformTeam.code) + + // if (await createPlatformTeam.error && await createPlatformTeam.code === 404) throw new ApolloError(await createPlatformTeam.error, 404) + // if (await createPlatformTeam.error && await createPlatformTeam.code === 409) throw new ApolloError(await createPlatformTeam.error, 409) + + const existingMember = await ctx.db.query.member({ where: { authId } }, `{id}`) .catch((err) => { - logger.debug('createTeam error: ') - throw new DatabaseError(`Team Creation Error: ${err}`) + logger.debug(err, 'existingMember error: ') + throw new DatabaseError(err) }) + + logger.debug('existingMember:') + logger.debug(await existingMember) + + let createTeam + if(existingMember !== null && existingMember.id !== null){ + logger.info('createTeam with existingMember:') + createTeam = await ctx.db.mutation.createTeam({ data: {name: name, slug: slug, owner: authId, members: {create: {member: {connect: {authId: authId}}, role: 'OWNER', status: { create: { status: 'ACTIVE', reason: `Connected to Team ${name}`}}}}, profile: {create: {avatar: avatar, banner: banner}}, fb: {create: {name: botName, slug: botSlug, kind:'TRADER', avatar: avatar, status: {create: {status: 'ACTIVE', reason: "Cloned on team creation"}}}}, status: {create: {status: 'ACTIVE', reason:"Team created"}}} }, TEAMS_FRAGMENT) + .catch((err) => { + logger.debug(err, 'createTeamMutation error: ') + return new DatabaseError(err) + }) + } else { + logger.info('createTeam without existingMember:') + createTeam = await ctx.db.mutation.createTeam({ data: {name: name, slug: slug, owner: authId, members: {create: {member: {create: {authId: authId, alias: alias, visible:'true', status: { create: { status: 'ACTIVE', reason: `Created team ${name}`}}}}, role: 'OWNER', status: { create: { status: 'ACTIVE', reason: `Created with Team ${name}`}}}}, profile: {create: {avatar: avatar, banner: banner}}, fb: {create: {name: botName, slug: botSlug, kind:'TRADER', avatar: avatar, status: {create: {status: 'ACTIVE', reason: "Cloned on team creation"}}}}, status: {create: {status: 'ACTIVE', reason:"Team created"}}} }, TEAMS_FRAGMENT) + .catch((err) => { + logger.debug(err, 'createTeamMutation error: ') + return new DatabaseError(err) + }) + } + + // sendTeamCreateConfirmation(email, name, botName) + + return createTeam }, async updateTeamProfile(parent, { slug, owner, description, motto, avatar, banner }, ctx, info) { return ctx.db.mutation.updateTeam({data:{profile: {update: {description: description, motto: motto, avatar: avatar, banner: banner}}}, where:{slug: slug}}, TEAMS_FRAGMENT) @@ -138,7 +129,8 @@ export const resolvers = { }) }, async deleteTeam(parent, { slug, botSlug }, ctx, info) { - logger.info('DeleteTeam resolver') + logger.info('deleteTeam ctx.request.user:') + logger.info(ctx.request.headers.userid) const authId = ctx.request.headers.userid if (!authId) { throw new AuthenticationError() diff --git a/api/src/graphql/resolvers/Query/Member.js b/api/src/graphql/resolvers/Query/Member.js index 2ca9d69d6..5f6e069b6 100644 --- a/api/src/graphql/resolvers/Query/Member.js +++ b/api/src/graphql/resolvers/Query/Member.js @@ -2,7 +2,7 @@ import { logger, AuthenticationError, DatabaseError } from '../../../logger' export const member = async (parent, arg, ctx, info) => { logger.info(' --> resolver.member') - logger.info(ctx.request) + logger.info(ctx.request.headers.userid) const authId = ctx.request.headers.userid if (!authId) { throw new AuthenticationError() @@ -17,7 +17,8 @@ export const member = async (parent, arg, ctx, info) => { export const owner = async (parent, args, ctx, info) => { logger.info('resolver.query.owner ctx: ') - const authId = ctx.request.userId + logger.info(ctx.request.headers.userid) + const authId = cctx.request.headers.userid if (!authId) { throw new AuthenticationError() } diff --git a/api/src/logger/errors.js b/api/src/logger/errors.js index 654dc98ef..14615ad7d 100644 --- a/api/src/logger/errors.js +++ b/api/src/logger/errors.js @@ -5,7 +5,12 @@ export class AuthenticationError extends Error { export class DatabaseError extends Error { code = 404 - message = 'Resource not found : ' + this.message + message = `Resource Error: ${this.message}` +} + +export class ApolloError extends Error { + code = 409 + message = `Resource already exists: ${this.message}` } export class WrongArgumentsError extends Error { diff --git a/api/src/logger/index.js b/api/src/logger/index.js index 4d3087ea9..e2b2cba06 100644 --- a/api/src/logger/index.js +++ b/api/src/logger/index.js @@ -1,9 +1,7 @@ import { createLogger, format, transports } from 'winston' import { inspect } from 'util' -import { AuthenticationError as ApolloAuthenticationError } from 'apollo-server-express' - -import { AuthenticationError, DatabaseError, WrongArgumentsError, ServiceUnavailableError } from './errors' +import { AuthenticationError, ApolloError, DatabaseError, WrongArgumentsError, ServiceUnavailableError, ConflictError } from './errors' class LoggerService { constructor() { @@ -32,4 +30,4 @@ class LoggerService { } export const logger = new LoggerService() // Default logger -export { LoggerService, AuthenticationError, ApolloAuthenticationError, DatabaseError, WrongArgumentsError, ServiceUnavailableError } +export { LoggerService, AuthenticationError, ApolloError, ConflictError, DatabaseError, WrongArgumentsError, ServiceUnavailableError } diff --git a/client/package.json b/client/package.json index 899741a35..258ac437d 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "@advancedalgos/teams-client", - "version": "0.0.23", + "version": "0.0.24", "description": "Advanced Algos Team client-side web app", "main": "src/index.js", "scripts": {