From 7a5a4e654a76b7a7e546d33b1e2ad4e3d6c9adc3 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Tue, 12 Jun 2018 17:12:27 -0400 Subject: [PATCH] bulk move/delete apis --- src/abstractions/api.service.ts | 4 ++ src/abstractions/cipher.service.ts | 2 + src/models/request/cipherBulkDeleteRequest.ts | 2 +- src/models/request/cipherBulkMoveRequest.ts | 2 +- src/services/api.service.ts | 44 +++++++++++++++++++ src/services/cipher.service.ts | 29 +++++++++++- 6 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/abstractions/api.service.ts b/src/abstractions/api.service.ts index 4bb7a854b6..c9c7792076 100644 --- a/src/abstractions/api.service.ts +++ b/src/abstractions/api.service.ts @@ -1,5 +1,7 @@ import { EnvironmentUrls } from '../models/domain/environmentUrls'; +import { CipherBulkDeleteRequest } from '../models/request/cipherBulkDeleteRequest'; +import { CipherBulkMoveRequest } from '../models/request/cipherBulkMoveRequest'; import { CipherCollectionsRequest } from '../models/request/cipherCollectionsRequest'; import { CipherRequest } from '../models/request/cipherRequest'; import { CipherShareRequest } from '../models/request/cipherShareRequest'; @@ -39,6 +41,8 @@ export abstract class ApiService { postCipher: (request: CipherRequest) => Promise; putCipher: (id: string, request: CipherRequest) => Promise; deleteCipher: (id: string) => Promise; + deleteManyCiphers: (request: CipherBulkDeleteRequest) => Promise; + putMoveCiphers: (request: CipherBulkMoveRequest) => Promise; putShareCipher: (id: string, request: CipherShareRequest) => Promise; putCipherCollections: (id: string, request: CipherCollectionsRequest) => Promise; postCipherAttachment: (id: string, data: FormData) => Promise; diff --git a/src/abstractions/cipher.service.ts b/src/abstractions/cipher.service.ts index 06e66d2df2..edcc6479f5 100644 --- a/src/abstractions/cipher.service.ts +++ b/src/abstractions/cipher.service.ts @@ -35,8 +35,10 @@ export abstract class CipherService { upsert: (cipher: CipherData | CipherData[]) => Promise; replace: (ciphers: { [id: string]: CipherData; }) => Promise; clear: (userId: string) => Promise; + moveManyWithServer: (ids: string[], folderId: string) => Promise; delete: (id: string | string[]) => Promise; deleteWithServer: (id: string) => Promise; + deleteManyWithServer: (ids: string[]) => Promise; deleteAttachment: (id: string, attachmentId: string) => Promise; deleteAttachmentWithServer: (id: string, attachmentId: string) => Promise; sortCiphersByLastUsed: (a: any, b: any) => number; diff --git a/src/models/request/cipherBulkDeleteRequest.ts b/src/models/request/cipherBulkDeleteRequest.ts index ccdf8ed22b..fb19523a20 100644 --- a/src/models/request/cipherBulkDeleteRequest.ts +++ b/src/models/request/cipherBulkDeleteRequest.ts @@ -2,6 +2,6 @@ export class CipherBulkDeleteRequest { ids: string[]; constructor(ids: string[]) { - this.ids = ids; + this.ids = ids == null ? [] : ids; } } diff --git a/src/models/request/cipherBulkMoveRequest.ts b/src/models/request/cipherBulkMoveRequest.ts index 0d78e4f165..c0e8c62673 100644 --- a/src/models/request/cipherBulkMoveRequest.ts +++ b/src/models/request/cipherBulkMoveRequest.ts @@ -3,7 +3,7 @@ export class CipherBulkMoveRequest { folderId: string; constructor(ids: string[], folderId: string) { - this.ids = ids; + this.ids = ids == null ? [] : ids; this.folderId = folderId; } } diff --git a/src/services/api.service.ts b/src/services/api.service.ts index 2f2d4f8308..9fb8b58248 100644 --- a/src/services/api.service.ts +++ b/src/services/api.service.ts @@ -6,6 +6,8 @@ import { TokenService } from '../abstractions/token.service'; import { EnvironmentUrls } from '../models/domain/environmentUrls'; +import { CipherBulkDeleteRequest } from '../models/request/cipherBulkDeleteRequest'; +import { CipherBulkMoveRequest } from '../models/request/cipherBulkMoveRequest'; import { CipherCollectionsRequest } from '../models/request/cipherCollectionsRequest'; import { CipherRequest } from '../models/request/cipherRequest'; import { CipherShareRequest } from '../models/request/cipherShareRequest'; @@ -354,6 +356,48 @@ export class ApiService implements ApiServiceAbstraction { } } + async deleteManyCiphers(request: CipherBulkDeleteRequest): Promise { + const authHeader = await this.handleTokenState(); + const response = await fetch(new Request(this.baseUrl + '/ciphers', { + body: JSON.stringify(request), + cache: 'no-cache', + credentials: this.getCredentials(), + headers: new Headers({ + 'Accept': 'application/json', + 'Authorization': authHeader, + 'Content-Type': 'application/json; charset=utf-8', + 'Device-Type': this.deviceType, + }), + method: 'DELETE', + })); + + if (response.status !== 200) { + const error = await this.handleError(response, false); + return Promise.reject(error); + } + } + + async putMoveCiphers(request: CipherBulkMoveRequest): Promise { + const authHeader = await this.handleTokenState(); + const response = await fetch(new Request(this.baseUrl + '/ciphers/move', { + body: JSON.stringify(request), + cache: 'no-cache', + credentials: this.getCredentials(), + headers: new Headers({ + 'Accept': 'application/json', + 'Authorization': authHeader, + 'Content-Type': 'application/json; charset=utf-8', + 'Device-Type': this.deviceType, + }), + method: 'PUT', + })); + + if (response.status !== 200) { + const error = await this.handleError(response, false); + return Promise.reject(error); + } + } + async putShareCipher(id: string, request: CipherShareRequest): Promise { const authHeader = await this.handleTokenState(); const response = await fetch(new Request(this.baseUrl + '/ciphers/' + id + '/share', { diff --git a/src/services/cipher.service.ts b/src/services/cipher.service.ts index e122b45283..523da8d33f 100644 --- a/src/services/cipher.service.ts +++ b/src/services/cipher.service.ts @@ -15,8 +15,11 @@ import { LoginUri } from '../models/domain/loginUri'; import { SecureNote } from '../models/domain/secureNote'; import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey'; +import { CipherBulkDeleteRequest } from '../models/request/cipherBulkDeleteRequest'; +import { CipherBulkMoveRequest } from '../models/request/cipherBulkMoveRequest'; import { CipherCollectionsRequest } from '../models/request/cipherCollectionsRequest'; import { CipherRequest } from '../models/request/cipherRequest'; +import { CipherShareRequest } from '../models/request/cipherShareRequest'; import { CipherResponse } from '../models/response/cipherResponse'; import { ErrorResponse } from '../models/response/errorResponse'; @@ -41,7 +44,6 @@ import { StorageService } from '../abstractions/storage.service'; import { UserService } from '../abstractions/user.service'; import { Utils } from '../misc/utils'; -import { CipherShareRequest } from '../models/request/cipherShareRequest'; const Keys = { ciphersPrefix: 'ciphers_', @@ -492,6 +494,26 @@ export class CipherService implements CipherServiceAbstraction { this.decryptedCipherCache = null; } + async moveManyWithServer(ids: string[], folderId: string): Promise { + await this.apiService.putMoveCiphers(new CipherBulkMoveRequest(ids, folderId)); + + const userId = await this.userService.getUserId(); + let ciphers = await this.storageService.get<{ [id: string]: CipherData; }>( + Keys.ciphersPrefix + userId); + if (ciphers == null) { + ciphers = {}; + } + + ids.forEach((id) => { + if (ciphers.hasOwnProperty(id)) { + ciphers[id].folderId = folderId; + } + }); + + await this.storageService.save(Keys.ciphersPrefix + userId, ciphers); + this.decryptedCipherCache = null; + } + async delete(id: string | string[]): Promise { const userId = await this.userService.getUserId(); const ciphers = await this.storageService.get<{ [id: string]: CipherData; }>( @@ -518,6 +540,11 @@ export class CipherService implements CipherServiceAbstraction { await this.delete(id); } + async deleteManyWithServer(ids: string[]): Promise { + await this.apiService.deleteManyCiphers(new CipherBulkDeleteRequest(ids)); + await this.delete(ids); + } + async deleteAttachment(id: string, attachmentId: string): Promise { const userId = await this.userService.getUserId(); const ciphers = await this.storageService.get<{ [id: string]: CipherData; }>(