From 5678c7d35a28f54723997d8b0ea46f720fa30942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Guti=C3=A9rrez?= <34506328+sg-gs@users.noreply.github.com> Date: Wed, 3 Jul 2024 16:17:51 +0200 Subject: [PATCH] Revert "[PB-2228]: feat/real time changes apn" --- .env.template | 6 - src/app/models/index.ts | 9 +- src/app/models/userNotificationTokens.ts | 60 ------ src/app/routes/routes.ts | 12 +- src/app/routes/storage.ts | 235 ++++++++++------------- src/app/services/user.js | 41 +--- src/config/initializers/apn.ts | 143 -------------- tests/controllers/storage.test.ts | 130 +------------ 8 files changed, 129 insertions(+), 507 deletions(-) delete mode 100644 src/app/models/userNotificationTokens.ts delete mode 100644 src/config/initializers/apn.ts diff --git a/.env.template b/.env.template index 77ea7a22..e9896cc8 100644 --- a/.env.template +++ b/.env.template @@ -39,9 +39,3 @@ STORJ_BRIDGE=http://network-api:6382 STRIPE_SK=sk_test_F3Ny2VGUnPga9FtyXkl7mzPc STRIPE_SK_TEST=sk_test_R3Ny2VG7nPgeJFrtXdt8mrPc SENDGRID_MODE_SANDBOX=false - -# APN -APN_BUNDLE_ID= -APN_TEAM_ID= -APN_KEY_ID= -APN_SECRET= \ No newline at end of file diff --git a/src/app/models/index.ts b/src/app/models/index.ts index fe3b69b7..79f22acd 100644 --- a/src/app/models/index.ts +++ b/src/app/models/index.ts @@ -26,7 +26,6 @@ import initLimit, { LimitModel } from './limit'; import initTier, { TierModel } from './tier'; import initPaidPlan, { PaidPlansModel } from './paidPlans'; import initTierLimit, { TierLimitsModel } from './tierLimit'; -import initUserNotificationTokens, { UserNotificationTokenModel } from './userNotificationTokens'; export type ModelType = | AppSumoModel @@ -54,8 +53,7 @@ export type ModelType = | PaidPlansModel | TierLimitsModel | LimitModel - | TierModel - | UserNotificationTokenModel; + | TierModel; export default (database: Sequelize) => { const AppSumo = initAppSumo(database); @@ -84,7 +82,6 @@ export default (database: Sequelize) => { const Tier = initTier(database); const PaidPlans = initPaidPlan(database); const TierLimit = initTierLimit(database); - const UserNotificationToken = initUserNotificationTokens(database); AppSumo.belongsTo(User); @@ -135,7 +132,6 @@ export default (database: Sequelize) => { User.hasMany(PrivateSharingFolder, { foreignKey: 'shared_with', sourceKey: 'uuid' }); User.hasMany(Sharings, { foreignKey: 'owner_id', sourceKey: 'uuid' }); User.hasMany(Sharings, { foreignKey: 'shared_with', sourceKey: 'uuid' }); - User.hasMany(UserNotificationToken, { foreignKey: 'userId', sourceKey: 'uuid' }); UserReferral.belongsTo(User, { foreignKey: 'user_id' }); UserReferral.belongsTo(Referral, { foreignKey: 'referral_id' }); @@ -174,8 +170,6 @@ export default (database: Sequelize) => { as: 'tiers', }); - UserNotificationToken.belongsTo(User, { foreignKey: 'userId', targetKey: 'uuid' }); - return { [AppSumo.name]: AppSumo, [Backup.name]: Backup, @@ -203,6 +197,5 @@ export default (database: Sequelize) => { [Limit.name]: Limit, [PaidPlans.name]: PaidPlans, [TierLimit.name]: TierLimit, - ['userNotificationToken']: UserNotificationToken, }; }; diff --git a/src/app/models/userNotificationTokens.ts b/src/app/models/userNotificationTokens.ts deleted file mode 100644 index fe8d73d3..00000000 --- a/src/app/models/userNotificationTokens.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { DataTypes, ModelDefined, Sequelize } from 'sequelize'; - -export interface UserNotificationTokenAttributes { - id: string; - userId: string; - token: string; - type: 'macos' | 'android' | 'ios'; - createdAt: Date; - updatedAt: Date; -} - -export type UserNotificationTokenModel = ModelDefined; - -export default (database: Sequelize): UserNotificationTokenModel => { - const UserNotificationToken: UserNotificationTokenModel = database.define( - 'user_notification_tokens', - { - id: { - type: DataTypes.UUID, - primaryKey: true, - defaultValue: DataTypes.UUIDV4, - }, - userId: { - type: DataTypes.STRING(36), - allowNull: false, - references: { - model: 'users', - key: 'uuid', - }, - }, - token: { - type: DataTypes.STRING, - allowNull: false, - }, - type: { - type: DataTypes.ENUM('macos', 'android', 'ios'), - allowNull: false, - }, - createdAt: { - field: 'created_at', - type: DataTypes.DATE, - allowNull: false, - defaultValue: DataTypes.NOW, - }, - updatedAt: { - field: 'updated_at', - type: DataTypes.DATE, - allowNull: false, - defaultValue: DataTypes.NOW, - }, - }, - { - tableName: 'user_notification_tokens', - timestamps: true, - underscored: true, - }, - ); - - return UserNotificationToken; -}; diff --git a/src/app/routes/routes.ts b/src/app/routes/routes.ts index 648582f8..4fb54168 100644 --- a/src/app/routes/routes.ts +++ b/src/app/routes/routes.ts @@ -25,7 +25,6 @@ import * as ReCaptchaV3 from '../../lib/recaptcha'; import * as AnalyticsService from '../../lib/analytics/AnalyticsService'; import { AuthorizedUser } from './types'; import { default as Notifications } from '../../config/initializers/notifications'; -import { default as Apn } from '../../config/initializers/apn'; const Logger = logger.getInstance(); @@ -33,7 +32,6 @@ export default (router: Router, service: any, App: any): Router => { service.Analytics = AnalyticsService; service.ReCaptcha = ReCaptchaV3; service.Notifications = Notifications.getInstance(); - service.Apn = Apn.getInstance(); AuthRoutes(router, service, App.config); ActivationRoutes(router, service); @@ -109,7 +107,7 @@ export default (router: Router, service: any, App: any): Router => { if (!userData.root_folder_id) { throw createHttpError(500, 'Account can not be initialized'); } - + const user = { email: userData.email, bucket: userData.bucket, @@ -123,9 +121,13 @@ export default (router: Router, service: any, App: any): Router => { res.status(200).send({ user }); } catch (err) { Logger.error( - `[AUTH/INITIALIZE] ERROR: ${(err as Error).message}, BODY ${JSON.stringify(req.body)}, STACK: ${ + `[AUTH/INITIALIZE] ERROR: ${ + (err as Error).message + }, BODY ${ + JSON.stringify(req.body) + }, STACK: ${ (err as Error).stack - }`, + }` ); return res.status(500).send({ error: 'Internal Server Error' }); diff --git a/src/app/routes/storage.ts b/src/app/routes/storage.ts index 2da12551..19250b1a 100644 --- a/src/app/routes/storage.ts +++ b/src/app/routes/storage.ts @@ -11,16 +11,16 @@ import { FileAttributes } from '../models/file'; import CONSTANTS from '../constants'; import { LockNotAvaliableError } from '../services/errors/locks'; import { ConnectionTimedOutError, UniqueConstraintError } from 'sequelize'; -import { - FileAlreadyExistsError, - FileWithNameAlreadyExistsError, +import { + FileAlreadyExistsError, + FileWithNameAlreadyExistsError } from '../services/errors/FileWithNameAlreadyExistsError'; -import { +import { FolderAlreadyExistsError, - FolderWithNameAlreadyExistsError, + FolderWithNameAlreadyExistsError } from '../services/errors/FolderWithNameAlreadyExistsError'; import * as resourceSharingMiddlewareBuilder from '../middleware/resource-sharing.middleware'; -import { validate } from 'uuid'; +import {validate } from 'uuid'; type AuthorizedRequest = Request & { user: UserAttributes }; interface Services { @@ -34,7 +34,6 @@ interface Services { Share: any; Crypt: any; Inxt: any; - Apn: any; } type SharedRequest = Request & { behalfUser: UserAttributes }; @@ -83,24 +82,23 @@ export class StorageController { const workspaceMembers = await this.services.User.findWorkspaceMembers(behalfUser.bridgeUser); - workspaceMembers.forEach(({ email, uuid }: { email: string; uuid: string }) => { - void this.services.Notifications.fileCreated({ - file: result, - email, - uuid, - clientId: clientId, - }); - - void this.getTokensAndSendNotification(uuid); - }); + workspaceMembers.forEach( + ({ email, uuid }: { email: string; uuid: string }) => + void this.services.Notifications.fileCreated({ + file: result, + email, + uuid, + clientId: clientId, + }), + ); } catch (err) { if (err instanceof FileAlreadyExistsError || err instanceof UniqueConstraintError) { return res.status(409).send({ error: 'File already exists' }); } this.logger.error( - `[FILE/CREATE] ERROR: ${(err as Error).message}, BODY ${JSON.stringify(file)}, STACK: ${ - (err as Error).stack - } USER: ${behalfUser.email}`, + `[FILE/CREATE] ERROR: ${(err as Error).message}, BODY ${ + JSON.stringify(file) + }, STACK: ${(err as Error).stack} USER: ${behalfUser.email}`, ); res.status(500).send({ error: 'Internal Server Error' }); } @@ -110,13 +108,16 @@ export class StorageController { const { behalfUser } = req as SharedRequest; const { file } = req.body as { file: { name: string; folderId: number; type: string } }; - if (!file || !file.name || !file.folderId || !file.type) { + if ( + !file || + !file.name || + !file.folderId || + !file.type + ) { this.logger.error( - `Missing body params to check the file existence for user ${behalfUser.email}: ${JSON.stringify( - file, - null, - 2, - )}`, + `Missing body params to check the file existence for user ${ + behalfUser.email + }: ${JSON.stringify(file, null, 2)}`, ); return res.status(400).json({ error: 'Missing information to check file existence' }); } @@ -131,9 +132,9 @@ export class StorageController { res.status(200).json(result.file); } catch (err) { this.logger.error( - `[FILE/CHECK-EXISTENCE] ERROR: ${(err as Error).message}, BODY ${JSON.stringify(file)}, STACK: ${ - (err as Error).stack - } USER: ${behalfUser.email}`, + `[FILE/CHECK-EXISTENCE] ERROR: ${ + (err as Error).message + }, BODY ${JSON.stringify(file)}, STACK: ${(err as Error).stack} USER: ${behalfUser.email}`, ); res.status(500).send({ error: 'Internal Server Error' }); } @@ -166,21 +167,21 @@ export class StorageController { if (parentFolder.userId !== user.id) { throw createHttpError(403, 'Parent folder does not belong to user'); } + return this.services.Folder.Create(user, folderName, parentFolderId, null, clientCreatedUuuid) .then(async (result: FolderAttributes) => { res.status(201).json(result); const workspaceMembers = await this.services.User.findWorkspaceMembers(user.bridgeUser); - workspaceMembers.forEach(({ email, uuid }: { uuid: string; email: string }) => { - void this.services.Notifications.folderCreated({ - folder: result, - email: email, - uuid, - clientId: clientId, - }); - - void this.getTokensAndSendNotification(uuid); - }); + workspaceMembers.forEach( + ({ email, uuid }: { uuid: string; email: string }) => + void this.services.Notifications.folderCreated({ + folder: result, + email: email, + uuid, + clientId: clientId, + }), + ); }) .catch((err: Error) => { if (err instanceof FolderAlreadyExistsError) { @@ -204,7 +205,7 @@ export class StorageController { } return this.services.Folder.CheckFolderExistence(user, name, parentId) - .then((result: { folder: FolderAttributes; exists: boolean }) => { + .then((result: { folder: FolderAttributes, exists: boolean }) => { if (result.exists) { res.status(200).json(result); } else { @@ -273,16 +274,15 @@ export class StorageController { res.status(204).send(result); this.services.User.findWorkspaceMembers(user.bridgeUser).then((workspaceMembers: any) => { - workspaceMembers.forEach(({ email, uuid }: { email: string; uuid: string }) => { - void this.services.Notifications.folderDeleted({ - id: folderId, - email: email, - uuid, - clientId: clientId, - }); - - void this.getTokensAndSendNotification(uuid); - }); + workspaceMembers.forEach( + ({ email, uuid }: { email: string; uuid: string }) => + void this.services.Notifications.folderDeleted({ + id: folderId, + email: email, + uuid, + clientId: clientId, + }), + ); }); } catch (error) { const err = error as Error; @@ -322,16 +322,15 @@ export class StorageController { .then(async (result: { result: FolderAttributes }) => { res.status(200).json(result); const workspaceMembers = await this.services.User.findWorkspaceMembers(user.bridgeUser); - workspaceMembers.forEach(({ email, uuid }: { email: string; uuid: string }) => { - void this.services.Notifications.folderUpdated({ - folder: result.result, - email: email, - uuid, - clientId: clientId, - }); - - void this.getTokensAndSendNotification(uuid); - }); + workspaceMembers.forEach( + ({ email, uuid }: { email: string; uuid: string }) => + void this.services.Notifications.folderUpdated({ + folder: result.result, + email: email, + uuid, + clientId: clientId, + }), + ); }) .catch((err: Error) => { if (err instanceof HttpError) { @@ -358,16 +357,15 @@ export class StorageController { .then(async (result: FolderAttributes) => { res.status(200).json(result); const workspaceMembers = await this.services.User.findWorkspaceMembers(user.bridgeUser); - workspaceMembers.forEach(({ email, uuid }: { email: string; uuid: string }) => { - void this.services.Notifications.folderUpdated({ - folder: result, - email: email, - uuid, - clientId: clientId, - }); - - void this.getTokensAndSendNotification(uuid); - }); + workspaceMembers.forEach( + ({ email, uuid }: { email: string; uuid: string }) => + void this.services.Notifications.folderUpdated({ + folder: result, + email: email, + uuid, + clientId: clientId, + }), + ); }) .catch((err: Error) => { this.logger.error(`Error updating metadata from folder ${folderId}: ${err}`); @@ -457,16 +455,15 @@ export class StorageController { .then(async (result: { result: FileAttributes }) => { res.status(200).json(result); const workspaceMembers = await this.services.User.findWorkspaceMembers(user.bridgeUser); - workspaceMembers.forEach(({ email, uuid }: { email: string; uuid: string }) => { - void this.services.Notifications.fileUpdated({ - file: result.result, - email: email, - uuid, - clientId: clientId, - }); - - void this.getTokensAndSendNotification(uuid); - }); + workspaceMembers.forEach( + ({ email, uuid }: { email: string; uuid: string }) => + void this.services.Notifications.fileUpdated({ + file: result.result, + email: email, + uuid, + clientId: clientId, + }), + ); }) .catch((err: Error) => { this.logger.error(err); @@ -501,16 +498,15 @@ export class StorageController { .then(async (result: FileAttributes) => { res.status(200).json(result); const workspaceMembers = await this.services.User.findWorkspaceMembers(user.bridgeUser); - workspaceMembers.forEach(({ email, uuid }: { email: string; uuid: string }) => { - void this.services.Notifications.fileUpdated({ - file: result, - email, - uuid, - clientId, - }); - - void this.getTokensAndSendNotification(uuid); - }); + workspaceMembers.forEach( + ({ email, uuid }: { email: string; uuid: string }) => + void this.services.Notifications.fileUpdated({ + file: result, + email, + uuid, + clientId, + }), + ); }) .catch((err: Error) => { this.logger.error(`Error updating metadata from file ${fileId} : ${err}`); @@ -570,16 +566,15 @@ export class StorageController { .then(async () => { res.status(200).json({ deleted: true }); const workspaceMembers = await this.services.User.findWorkspaceMembers(user.bridgeUser); - workspaceMembers.forEach(({ email, uuid }: { email: string; uuid: string }) => { - void this.services.Notifications.fileDeleted({ - id: Number(fileid), - email, - uuid, - clientId, - }); - - void this.getTokensAndSendNotification(uuid); - }); + workspaceMembers.forEach( + ({ email, uuid }: { email: string; uuid: string }) => + void this.services.Notifications.fileDeleted({ + id: Number(fileid), + email, + uuid, + clientId, + }), + ); this.services.Analytics.trackFileDeleted(req); }) @@ -807,28 +802,6 @@ export class StorageController { res.status(200).json(result); } - - public async getTokensAndSendNotification(userUuid: string) { - const tokens = await this.services.User.getUserNotificationTokens(userUuid, 'macos'); - - const tokenPromises = tokens.map(async ({ token }: { token: string }) => { - try { - const response = await this.services.Apn.sendStorageNotification(token, userUuid); - return response.statusCode === 410 ? token : null; - } catch (error) { - this.logger.error(`Error sending APN notification to ${userUuid}: ${error}`); - return null; - } - }); - - const results = await Promise.all(tokenPromises); - - const expiredTokens = results.filter((token) => token !== null); - - if (expiredTokens.length > 0) { - await this.services.User.deleteUserNotificationTokens(userUuid, expiredTokens); - } - } } export default (router: Router, service: any) => { @@ -838,20 +811,22 @@ export default (router: Router, service: any) => { const resourceSharingAdapter = resourceSharingMiddlewareBuilder.build(service); const controller = new StorageController(service, Logger); - router.post( - '/storage/file', + router.post('/storage/file', passportAuth, sharedAdapter, resourceSharingAdapter.UploadFile, - controller.createFile.bind(controller), + controller.createFile.bind(controller) ); - router.post('/storage/file/exists', passportAuth, sharedAdapter, controller.checkFileExistence.bind(controller)); - router.post( - '/storage/thumbnail', + router.post('/storage/file/exists', + passportAuth, + sharedAdapter, + controller.checkFileExistence.bind(controller) + ); + router.post('/storage/thumbnail', passportAuth, sharedAdapter, resourceSharingAdapter.UploadThumbnail, - controller.createThumbnail.bind(controller), + controller.createThumbnail.bind(controller) ); router.post('/storage/folder', passportAuth, sharedAdapter, controller.createFolder.bind(controller)); router.post('/storage/folder/exists', passportAuth, sharedAdapter, controller.checkFolderExistence.bind(controller)); @@ -872,7 +847,7 @@ export default (router: Router, service: any) => { passportAuth, sharedAdapter, resourceSharingAdapter.RenameFile, - controller.updateFile.bind(controller), + controller.updateFile.bind(controller) ); router.delete('/storage/bucket/:bucketid/file/:fileid', passportAuth, controller.deleteFileBridge.bind(controller)); router.delete( diff --git a/src/app/services/user.js b/src/app/services/user.js index 8958620c..2707599a 100644 --- a/src/app/services/user.js +++ b/src/app/services/user.js @@ -307,15 +307,12 @@ module.exports = (Model, App) => { await user.destroy(); - await Model.folder.update( - { deleted: true, removed: true }, - { - where: { - user_id: user.id, - parent_id: null, - }, - }, - ); + await Model.folder.update({ deleted: true, removed: true }, { + where: { + user_id: user.id, + parent_id: null, + } + }); logger.info('User %s confirmed deactivation', userEmail); } catch (err) { @@ -360,7 +357,7 @@ module.exports = (Model, App) => { password: newPassword, mnemonic, hKey: newSalt, - lastPasswordChangedAt: new Date(), + lastPasswordChangedAt: new Date() }, { where: { username: { [Op.eq]: user.email } }, @@ -524,7 +521,7 @@ module.exports = (Model, App) => { attributes: [[fn('sum', col('size')), 'total']], raw: true, }); - + driveUsage = parseInt(usage[0].total); await Redis.setUsage(user.uuid, driveUsage); @@ -799,26 +796,6 @@ module.exports = (Model, App) => { } }; - const getUserNotificationTokens = async (userUuid, type = null) => { - let whereClause = { userId: userUuid }; - - if (type !== null) { - whereClause.type = type; - } - return Model.userNotificationToken.findAll({ where: whereClause }); - }; - - const deleteUserNotificationTokens = async (userUuid, tokens) => { - return Model.userNotificationToken.destroy({ - where: { - userId: userUuid, - token: { - [Op.in]: tokens, - }, - }, - }); - }; - return { Name: 'User', FindOrCreate, @@ -855,7 +832,5 @@ module.exports = (Model, App) => { sendEmailVerification, verifyEmail, updateTier, - getUserNotificationTokens, - deleteUserNotificationTokens, }; }; diff --git a/src/config/initializers/apn.ts b/src/config/initializers/apn.ts deleted file mode 100644 index 014e3fd9..00000000 --- a/src/config/initializers/apn.ts +++ /dev/null @@ -1,143 +0,0 @@ -import * as http2 from 'http2'; -import jwt, { JwtHeader } from 'jsonwebtoken'; -import Logger from '../../lib/logger'; - -export default class Apn { - private static instance: Apn; - private client: http2.ClientHttp2Session; - private readonly maxReconnectAttempts = 3; - private reconnectAttempts = 0; - private reconnectDelay = 1000; - private readonly bundleId = process.env.APN_BUNDLE_ID; - - private readonly apnUrl = process.env.NODE_ENV as string; - - private jwt: string | null = null; - private jwtGeneratedAt = 0; - - constructor() { - this.client = this.connectToAPN(); - } - - static getInstance(): Apn { - if (!Apn.instance) { - Apn.instance = new Apn(); - } - return Apn.instance; - } - - private connectToAPN(): http2.ClientHttp2Session { - const apnSecret = process.env.APN_SECRET; - const apnKeyId = process.env.APN_KEY_ID; - const apnTeamId = process.env.APN_TEAM_ID; - - if (!apnSecret || !apnKeyId || !apnTeamId || !this.apnUrl) { - Logger.getInstance().warn('APN env variables are not defined'); - } - - const client = http2.connect(this.apnUrl); - - client.on('error', (err) => { - Logger.getInstance().error('APN connection error', err); - }); - client.on('close', () => { - Logger.getInstance().warn('APN connection was closed'); - this.handleReconnect(); - }); - client.on('connect', () => { - Logger.getInstance().info('Connected to APN'); - }); - - return client; - } - - private generateJwt(): string { - if (!process.env.APN_SECRET || !process.env.APN_KEY_ID || !process.env.APN_TEAM_ID) { - throw new Error('Undefined APN env variables, necessary for JWT generation'); - } - if (this.jwt && Date.now() - this.jwtGeneratedAt < 3600) { - return this.jwt; - } - - this.jwt = jwt.sign( - { - iss: process.env.APN_TEAM_ID, - iat: Math.floor(Date.now() / 1000), - }, - Buffer.from(process.env.APN_SECRET, 'base64').toString('utf8'), - { - algorithm: 'ES256', - header: { - alg: 'ES256', - kid: process.env.APN_KEY_ID, - } as JwtHeader, - }, - ); - - this.jwtGeneratedAt = Date.now(); - - return this.jwt; - } - - private handleReconnect() { - if (this.reconnectAttempts < this.maxReconnectAttempts) { - setTimeout(() => { - Logger.getInstance().info(`Attempting to reconnect to APN (#${this.reconnectAttempts + 1})`); - this.connectToAPN(); - this.reconnectAttempts++; - }, this.reconnectDelay * Math.pow(2, this.reconnectAttempts)); - } else { - Logger.getInstance().error('Maximum APN reconnection attempts reached'); - } - } - - public sendStorageNotification(deviceToken: string, userUuid: string): Promise<{ statusCode: number; body: string }> { - return new Promise((resolve, reject) => { - if (!this.client || this.client.closed) { - this.connectToAPN(); - } - - const headers: http2.OutgoingHttpHeaders = { - [http2.constants.HTTP2_HEADER_METHOD]: 'POST', - [http2.constants.HTTP2_HEADER_PATH]: `/3/device/${deviceToken}`, - [http2.constants.HTTP2_HEADER_SCHEME]: 'https', - [http2.constants.HTTP2_HEADER_AUTHORITY]: 'api.push.apple.com', - [http2.constants.HTTP2_HEADER_CONTENT_TYPE]: 'application/json', - [http2.constants.HTTP2_HEADER_AUTHORIZATION]: `bearer ${this.generateJwt()}`, - 'apns-topic': `${this.bundleId}.pushkit.fileprovider`, - }; - - const req = this.client.request({ ...headers }); - - req.setEncoding('utf8'); - req.write( - JSON.stringify({ - 'container-identifier': 'NSFileProviderWorkingSetContainerItemIdentifier', - domain: userUuid, - }), - ); - - req.end(); - - let statusCode = 0; - let data = ''; - - req.on('response', (_, status) => { - statusCode = status || 0; - }); - - req.on('data', (chunk) => { - data += chunk; - }); - - req.on('end', () => { - resolve({ statusCode, body: data }); - }); - - req.on('error', (err) => { - Logger.getInstance().error('APN request error', err); - reject(new Error(err)); - }); - }); - } -} diff --git a/tests/controllers/storage.test.ts b/tests/controllers/storage.test.ts index cdcae70f..ba7ff9ad 100644 --- a/tests/controllers/storage.test.ts +++ b/tests/controllers/storage.test.ts @@ -227,14 +227,10 @@ describe('Storage controller', () => { }, User: { findWorkspaceMembers: stubOf('findWorkspaceMembers').resolves([{}, {}]), - getUserNotificationTokens: sinon.stub().resolves([{ token: 'token' }]), }, Notifications: { fileCreated: sinon.spy(), }, - Apn: { - sendStorageNotification: sinon.stub().resolves(Promise.resolve({ statusCode: 200, body: 'ok' })), - }, }; const controller = getController(services); const request = getRequest({ @@ -271,7 +267,6 @@ describe('Storage controller', () => { expect(services.Analytics.trackUploadCompleted.calledOnce).to.be.true; expect(services.User.findWorkspaceMembers.calledOnce).to.be.true; expect(services.Notifications.fileCreated.calledTwice).to.be.true; - expect(services.Apn.sendStorageNotification.calledTwice).to.be.true; expect(jsonSpy.calledOnce).to.be.true; }); @@ -286,14 +281,10 @@ describe('Storage controller', () => { }, User: { findWorkspaceMembers: stubOf('findWorkspaceMembers').resolves([{}, {}]), - getUserNotificationTokens: sinon.stub().resolves([{ token: 'token' }]), }, Notifications: { fileCreated: sinon.spy(), }, - Apn: { - sendStorageNotification: sinon.stub().resolves(Promise.resolve({ statusCode: 200, body: 'ok' })), - }, }; const controller = getController(services); const request = getRequest({ @@ -331,7 +322,6 @@ describe('Storage controller', () => { expect(services.Analytics.trackUploadCompleted.calledOnce).to.be.true; expect(services.User.findWorkspaceMembers.calledOnce).to.be.true; expect(services.Notifications.fileCreated.calledTwice).to.be.true; - expect(services.Apn.sendStorageNotification.calledTwice).to.be.true; expect(jsonSpy.calledOnce).to.be.true; }); @@ -349,14 +339,10 @@ describe('Storage controller', () => { }, User: { findWorkspaceMembers: stubOf('findWorkspaceMembers').resolves([{}, {}]), - getUserNotificationTokens: sinon.stub().resolves([{ token: 'token' }]), }, Notifications: { fileCreated: sinon.spy(), }, - Apn: { - sendStorageNotification: sinon.stub().resolves(Promise.resolve({ statusCode: 200, body: 'ok' })), - }, }; const controller = getController(services); const request = getRequest({ @@ -394,7 +380,6 @@ describe('Storage controller', () => { expect(services.Analytics.trackUploadCompleted.calledOnce).to.be.true; expect(services.User.findWorkspaceMembers.calledOnce).to.be.true; expect(services.Notifications.fileCreated.calledTwice).to.be.true; - expect(services.Apn.sendStorageNotification.calledTwice).to.be.true; expect(jsonSpy.calledOnce).to.be.true; expect(services.UsersReferrals.applyUserReferral.calledOnce).to.be.true; expect(services.UsersReferrals.applyUserReferral.args[0]).to.deep.equal(['id', 'install-mobile-app']); @@ -414,14 +399,10 @@ describe('Storage controller', () => { }, User: { findWorkspaceMembers: stubOf('findWorkspaceMembers').resolves([{}, {}]), - getUserNotificationTokens: sinon.stub().resolves([{ token: 'token' }]), }, Notifications: { fileCreated: sinon.spy(), }, - Apn: { - sendStorageNotification: sinon.stub().resolves(Promise.resolve({ statusCode: 200, body: 'ok' })), - }, }; const controller = getController(services); const request = getRequest({ @@ -459,7 +440,6 @@ describe('Storage controller', () => { expect(services.Analytics.trackUploadCompleted.calledOnce).to.be.true; expect(services.User.findWorkspaceMembers.calledOnce).to.be.true; expect(services.Notifications.fileCreated.calledTwice).to.be.true; - expect(services.Apn.sendStorageNotification.calledTwice).to.be.true; expect(jsonSpy.calledOnce).to.be.true; expect(services.UsersReferrals.applyUserReferral.calledOnce).to.be.true; expect(services.UsersReferrals.applyUserReferral.args[0]).to.deep.equal(['id', 'install-desktop-app']); @@ -569,14 +549,10 @@ describe('Storage controller', () => { }, User: { findWorkspaceMembers: stubOf('findWorkspaceMembers').resolves([{}, {}]), - getUserNotificationTokens: sinon.stub().resolves([{ token: 'token' }]), }, Notifications: { folderCreated: sinon.spy(), }, - Apn: { - sendStorageNotification: sinon.stub().resolves(Promise.resolve({ statusCode: 200, body: 'ok' })), - }, }; const controller = getController(services); const request = getRequest({ @@ -605,7 +581,6 @@ describe('Storage controller', () => { expect(services.Folder.Create.calledOnce).to.be.true; expect(services.User.findWorkspaceMembers.calledOnce).to.be.true; expect(services.Notifications.folderCreated.calledTwice).to.be.true; - expect(services.Apn.sendStorageNotification.calledTwice).to.be.true; expect(jsonSpy.calledOnce).to.be.true; expect(jsonSpy.args[0]).to.deep.equal([ { @@ -868,14 +843,10 @@ describe('Storage controller', () => { }, User: { findWorkspaceMembers: stubOf('findWorkspaceMembers').resolves([{}, {}]), - getUserNotificationTokens: stubOf('getUserNotificationTokens').resolves([{ token: 'token' }]), }, Notifications: { folderDeleted: sinon.spy(), }, - Apn: { - sendStorageNotification: sinon.stub().resolves(Promise.resolve({ statusCode: 200, body: 'ok' })), - }, }; const controller = getController(services); const request = getRequest({ @@ -901,7 +872,6 @@ describe('Storage controller', () => { expect(services.Folder.Delete.calledOnce).to.be.true; expect(services.User.findWorkspaceMembers.calledOnce).to.be.true; expect(services.Notifications.folderDeleted.calledTwice).to.be.true; - expect(services.Apn.sendStorageNotification.calledTwice).to.be.true; expect(sendSpy.calledOnce).to.be.true; expect(sendSpy.args[0]).to.deep.equal([ { @@ -1003,14 +973,10 @@ describe('Storage controller', () => { }, User: { findWorkspaceMembers: stubOf('findWorkspaceMembers').resolves([{}, {}]), - getUserNotificationTokens: sinon.stub().resolves([{ token: 'token' }]), }, Notifications: { folderUpdated: sinon.spy(), }, - Apn: { - sendStorageNotification: sinon.stub().resolves(Promise.resolve({ statusCode: 200, body: 'ok' })), - }, }; const controller = getController(services); const request = getRequest({ @@ -1037,7 +1003,6 @@ describe('Storage controller', () => { expect(services.Folder.MoveFolder.calledOnce).to.be.true; expect(services.User.findWorkspaceMembers.calledOnce).to.be.true; expect(services.Notifications.folderUpdated.calledTwice).to.be.true; - expect(services.Apn.sendStorageNotification.calledTwice).to.be.true; expect(jsonSpy.calledOnce).to.be.true; expect(jsonSpy.args[0]).to.deep.equal([ { @@ -1127,14 +1092,10 @@ describe('Storage controller', () => { }, User: { findWorkspaceMembers: stubOf('findWorkspaceMembers').resolves([{}, {}]), - getUserNotificationTokens: sinon.stub().resolves([{ token: 'token' }]), }, Notifications: { folderUpdated: sinon.spy(), }, - Apn: { - sendStorageNotification: sinon.stub().resolves(Promise.resolve({ statusCode: 200, body: 'ok' })), - }, }; const controller = getController(services); const request = getRequest({ @@ -1163,7 +1124,6 @@ describe('Storage controller', () => { expect(services.Folder.UpdateMetadata.calledOnce).to.be.true; expect(services.User.findWorkspaceMembers.calledOnce).to.be.true; expect(services.Notifications.folderUpdated.calledTwice).to.be.true; - expect(services.Apn.sendStorageNotification.calledTwice).to.be.true; expect(jsonSpy.calledOnce).to.be.true; expect(jsonSpy.args[0]).to.deep.equal([ { @@ -1563,14 +1523,10 @@ describe('Storage controller', () => { }, User: { findWorkspaceMembers: stubOf('findWorkspaceMembers').resolves([{}, {}]), - getUserNotificationTokens: sinon.stub().resolves([{ token: 'token' }]), }, Notifications: { fileUpdated: sinon.spy(), }, - Apn: { - sendStorageNotification: sinon.stub().resolves(Promise.resolve({ statusCode: 200, body: 'ok' })), - }, }; const controller = getController(services); const request = getRequest({ @@ -1597,7 +1553,6 @@ describe('Storage controller', () => { expect(services.Files.MoveFile.calledOnce).to.be.true; expect(services.User.findWorkspaceMembers.calledOnce).to.be.true; expect(services.Notifications.fileUpdated.calledTwice).to.be.true; - expect(services.Apn.sendStorageNotification.calledTwice).to.be.true; expect(jsonSpy.calledOnce).to.be.true; expect(jsonSpy.args[0]).to.deep.equal([ { @@ -1771,14 +1726,10 @@ describe('Storage controller', () => { }, User: { findWorkspaceMembers: stubOf('findWorkspaceMembers').resolves([{}, {}]), - getUserNotificationTokens: sinon.stub().resolves([{ token: 'token' }]), }, Notifications: { fileUpdated: sinon.spy(), }, - Apn: { - sendStorageNotification: sinon.stub().resolves(Promise.resolve({ statusCode: 200, body: 'ok' })), - }, }; const controller = getController(services); const request = getRequest({ @@ -2074,14 +2025,10 @@ describe('Storage controller', () => { }, User: { findWorkspaceMembers: stubOf('findWorkspaceMembers').resolves([{}, {}]), - getUserNotificationTokens: sinon.stub().resolves([{ token: 'token' }]), }, Notifications: { fileDeleted: sinon.spy(), }, - Apn: { - sendStorageNotification: sinon.stub().resolves(Promise.resolve({ statusCode: 200, body: 'ok' })), - }, Analytics: { trackFileDeleted: sinon.spy(), }, @@ -2836,67 +2783,9 @@ describe('Storage controller', () => { ]); }); }); - describe('getTokensAndSendNotification', () => { - it('When no tokens are found apn should not be called', async () => { - const services = { - User: { - getUserNotificationTokens: sinon.stub().resolves([]), - }, - Apn: { - sendStorageNotification: sinon.stub(), - }, - }; - - const controller = getController(services); - - await controller.getTokensAndSendNotification('userId'); - - expect(services.User.getUserNotificationTokens.calledOnce).to.be.true; - expect(services.Apn.sendStorageNotification.called).to.be.false; - }); - - it('When APN returns 410 the token should be deleted', async () => { - const services = { - User: { - getUserNotificationTokens: sinon.stub().resolves(['token']), - deleteUserNotificationTokens: sinon.stub().resolves(1), - }, - Apn: { - sendStorageNotification: sinon.stub().resolves(Promise.resolve({ statusCode: 410, body: 'Expired token' })), - }, - }; - - const controller = getController(services); - - await controller.getTokensAndSendNotification('userId'); - - expect(services.User.getUserNotificationTokens.calledOnce).to.be.true; - expect(services.Apn.sendStorageNotification.calledOnce).to.be.true; - expect(services.User.deleteUserNotificationTokens.calledOnce).to.be.true; - }); - }); - - it('When APN returns 200 the token should not be deleted', async () => { - const services = { - User: { - getUserNotificationTokens: sinon.stub().resolves([{ token: 'token' }]), - deleteUserNotificationTokens: sinon.spy(), - }, - Apn: { - sendStorageNotification: sinon.stub().resolves(Promise.resolve({ statusCode: 200, body: 'ok' })), - }, - }; - - const controller = getController(services); - - await controller.getTokensAndSendNotification('userId'); - - expect(services.User.getUserNotificationTokens.calledOnce).to.be.true; - expect(services.Apn.sendStorageNotification.calledOnce).to.be.true; - expect(services.User.deleteUserNotificationTokens.called).to.be.false; - }); }); + function getController(services = {}, logger = {}): StorageController { const defaultServices = { Files: {}, @@ -2912,17 +2801,17 @@ function getController(services = {}, logger = {}): StorageController { const finalServices = { ...defaultServices, - ...services, + ...services }; const defaultLogger = { error: () => null, - warn: () => null, + warn: () => null }; const finalLogger = { ...defaultLogger, - ...logger, + ...logger } as unknown as Logger; return new StorageController(finalServices, finalLogger); @@ -2937,10 +2826,7 @@ function getResponse(props = {}): Response { } function stubOf(functionName: string): SinonStub { - return sinon.stub( - { - [functionName]: null, - }, - functionName, - ); -} + return sinon.stub({ + [functionName]: null + }, functionName); +} \ No newline at end of file