Mirror of https://github.com/roostorg/coop github.com/roostorg/coop
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 557ff54b2b435e5f1e789c6a8a4e1bebf2d7deb6 296 lines 9.5 kB view raw
1import { mergeResolvers } from '@graphql-tools/merge'; 2import { AuthenticationError } from 'apollo-server-core'; 3import { type GraphQLFieldResolver } from 'graphql'; 4import { type PassportContext } from 'graphql-passport'; 5 6import { type GQLServices } from '../api.js'; 7import { type DataSources } from '../iocContainer/index.js'; 8import { type User } from '../models/UserModel.js'; 9import { CoopError, isCoopErrorOfType } from '../utils/errors.js'; 10import { 11 type GQLInviteUserToken, 12 type GQLMutationResolvers, 13 type GQLQueryResolvers, 14} from './generated.js'; 15import { resolvers as actionResolvers } from './modules/action.js'; 16import { resolvers as actionStatisticsResolvers } from './modules/actionStatistics.js'; 17import { resolvers as apiKeyResolvers } from './modules/apiKey.js'; 18import { resolvers as authenticationResolvers } from './modules/authentication.js'; 19import { resolvers as backtestResolvers } from './modules/backtest.js'; 20import { resolvers as contentTypeResolvers } from './modules/contentType.js'; 21import { resolvers as genericResolvers } from './modules/generic.js'; 22import { resolvers as insightsResolvers } from './modules/insights.js'; 23import { resolvers as integrationResolvers } from './modules/integration.js'; 24import { resolvers as investigationResolvers } from './modules/investigation.js'; 25import { resolvers as itemTypeResolvers } from './modules/itemType.js'; 26import { resolvers as locationBankResolvers } from './modules/locationBank.js'; 27import { resolvers as manualReviewToolResolvers } from './modules/manualReviewTool.js'; 28import { resolvers as hashBankResolvers } from './modules/hashBanks/resolvers.js'; 29import { resolvers as ncmecResolvers } from './modules/ncmec.js'; 30import { resolvers as orgResolvers } from './modules/org.js'; 31import { resolvers as policyResolvers } from './modules/policy.js'; 32import { resolvers as reportingResolvers } from './modules/reporting.js'; 33import { resolvers as reportingRulesResolvers } from './modules/reportingRule.js'; 34import { resolvers as retroactionResolvers } from './modules/retroaction.js'; 35import { resolvers as routingRulesResolvers } from './modules/routingRule.js'; 36import { resolvers as ruleResolvers } from './modules/rule.js'; 37import { resolvers as signalResolvers } from './modules/signal.js'; 38import { resolvers as spotTestResolvers } from './modules/spotTest.js'; 39import { resolvers as textBankResolvers } from './modules/textBank.js'; 40import { resolvers as userResolvers } from './modules/user.js'; 41import { gqlErrorResult, gqlSuccessResult } from './utils/gqlResult.js'; 42 43// eslint-disable-next-line @typescript-eslint/no-restricted-types 44export type Context = PassportContext<User, {}> & { 45 dataSources: DataSources; 46 services: GQLServices; 47}; 48 49export type Resolver<Source = unknown, Args = unknown> = GraphQLFieldResolver< 50 Source, 51 Context, 52 Args 53>; 54 55export type ResolverMap<Source = unknown> = { 56 // The `any` here lets us use a different + arbitrary type for 57 // the args object in each resolver, which is what we need. 58 // eslint-disable-next-line @typescript-eslint/no-explicit-any 59 [key: string]: Resolver<Source, any> | ResolverMap<Source>; 60}; 61 62/** 63 * GraphQL Query Resolvers 64 */ 65const Query: GQLQueryResolvers = { 66 async myOrg(_, __, context) { 67 const user = context.getUser(); 68 if (user == null) { 69 return null; 70 } 71 return context.dataSources.orgAPI.getGraphQLOrgFromId(user.orgId); 72 }, 73 async inviteUserToken(_, { token }, { dataSources }) { 74 try { 75 const inviteUserToken = await dataSources.orgAPI.getInviteUserToken( 76 token, 77 ); 78 return gqlSuccessResult( 79 { tokenData: inviteUserToken as unknown as GQLInviteUserToken }, 80 'InviteUserTokenSuccessResponse', 81 ); 82 } catch (e: unknown) { 83 if ( 84 isCoopErrorOfType(e, [ 85 'InviteUserTokenExpiredError', 86 'InviteUserTokenMissingError', 87 ]) 88 ) { 89 return gqlErrorResult(e); 90 } 91 92 throw e; 93 } 94 }, 95 async allRuleInsights(_, __, context) { 96 const user = context.getUser(); 97 if (user == null) { 98 return null; 99 } 100 101 try { 102 // TODO: this response type actually isn't right; remove cast and fix errors. 103 return (await context.dataSources.ruleAPI.getAllRuleInsights( 104 user.orgId, 105 )) as any; 106 } catch (e) { 107 // eslint-disable-next-line no-console 108 console.error('allRuleInsights: warehouse query failed:', (e as Error).message); 109 return null; 110 } 111 }, 112 async isWarehouseAvailable(_, __, context) { 113 try { 114 await context.services.DataWarehouse.query( 115 'SELECT 1', 116 context.services.Tracer, 117 ); 118 return true; 119 } catch (e) { 120 // eslint-disable-next-line no-console 121 console.error('isWarehouseAvailable: warehouse health check failed:', (e as Error).message); 122 return false; 123 } 124 }, 125}; 126 127type TSignUpResponse = { data: User } | CoopError; 128const SignUpResponse: ResolverMap<TSignUpResponse> = { 129 __resolveType(response) { 130 if (response instanceof CoopError) { 131 return 'SignUpUserExistsError'; 132 } else { 133 return 'SignUpSuccessResponse'; 134 } 135 }, 136}; 137 138const Mutation: GQLMutationResolvers = { 139 async signUp(_, params, context) { 140 try { 141 const newUser = await context.dataSources.userAPI.signUp(params, context); 142 return { data: newUser }; 143 } catch (e: unknown) { 144 if (isCoopErrorOfType(e, 'SignUpUserExistsError')) { 145 return gqlErrorResult(e); 146 } 147 148 throw e; 149 } 150 }, 151 async sendPasswordReset(_, params, context) { 152 const { email } = params.input; 153 context.services.UserManagementService.sendPasswordResetEmail({ 154 email, 155 }).catch(() => {}); 156 return true; 157 }, 158 async resetPassword(_, params, context) { 159 const { token, newPassword } = params.input; 160 await context.services.UserManagementService.resetPasswordForToken({ 161 token, 162 newPassword, 163 }); 164 return true; 165 }, 166 async generatePasswordResetToken(_, { userId }, context) { 167 const user = context.getUser(); 168 if (user == null) { 169 throw new AuthenticationError('Authenticated user required'); 170 } 171 172 if (!user.getPermissions().includes('MANAGE_ORG')) { 173 throw new AuthenticationError( 174 'User does not have permission to generate password reset tokens', 175 ); 176 } 177 178 const token = 179 await context.services.UserManagementService.generatePasswordResetTokenForUser( 180 { userId, invokerOrgId: user.orgId }, 181 ); 182 return token; 183 }, 184 async updateRole(_, params, context) { 185 const user = context.getUser(); 186 if (user == null) { 187 throw new AuthenticationError('Authenticated user required'); 188 } 189 190 await context.services.UserManagementService.updateUserRole({ 191 userId: params.input.id, 192 newRole: params.input.role, 193 orgId: user.orgId, 194 invoker: { 195 userId: user.id, 196 orgId: user.orgId, 197 permissions: user.getPermissions(), 198 }, 199 }); 200 201 return true; 202 }, 203 async inviteUser(_, params, context) { 204 const user = context.getUser(); 205 if (user == null) { 206 throw new AuthenticationError('Authenticated user required'); 207 } 208 209 if (!user.getPermissions().includes('MANAGE_ORG')) { 210 throw new AuthenticationError( 211 'User does not have permission to invite users', 212 ); 213 } 214 215 const token = await context.dataSources.orgAPI.inviteUser( 216 params.input, 217 user.orgId, 218 ); 219 return token; 220 }, 221 async deleteInvite(_, { id }, context) { 222 const user = context.getUser(); 223 if (user == null) { 224 throw new AuthenticationError('Authenticated user required'); 225 } 226 227 if (!user.getPermissions().includes('MANAGE_ORG')) { 228 throw new AuthenticationError( 229 'User does not have permission to delete invites', 230 ); 231 } 232 233 return context.services.UserManagementService.deleteInvite(id, user.orgId); 234 }, 235 async approveUser(_, { id }, context) { 236 const user = context.getUser(); 237 if (user == null) { 238 throw new AuthenticationError('Authenticated user required'); 239 } 240 241 if (!user.getPermissions().includes('MANAGE_ORG')) { 242 throw new AuthenticationError( 243 'User does not have permission to approve users', 244 ); 245 } 246 247 return context.dataSources.userAPI.approveUser(id, user.orgId); 248 }, 249 async rejectUser(_, { id }, context) { 250 const user = context.getUser(); 251 if (user == null) { 252 throw new AuthenticationError('Authenticated user required'); 253 } 254 255 if (!user.getPermissions().includes('MANAGE_ORG')) { 256 throw new AuthenticationError( 257 'User does not have permission to reject users', 258 ); 259 } 260 261 return context.dataSources.userAPI.rejectUser(id, user.orgId); 262 }, 263 async requestDemo(_, params, context) { 264 return context.dataSources.orgAPI.requestDemo(params.input); 265 }, 266}; 267 268export default mergeResolvers([ 269 { Query, Mutation, SignUpResponse }, 270 actionResolvers, 271 actionStatisticsResolvers, 272 apiKeyResolvers, 273 authenticationResolvers, 274 backtestResolvers, 275 contentTypeResolvers, 276 genericResolvers, 277 insightsResolvers, 278 integrationResolvers, 279 investigationResolvers, 280 itemTypeResolvers, 281 locationBankResolvers, 282 manualReviewToolResolvers, 283 hashBankResolvers, 284 ncmecResolvers, 285 orgResolvers, 286 policyResolvers, 287 reportingResolvers, 288 reportingRulesResolvers, 289 retroactionResolvers, 290 routingRulesResolvers, 291 ruleResolvers, 292 signalResolvers, 293 spotTestResolvers, 294 textBankResolvers, 295 userResolvers, 296]);