import angular from 'angular' ; import { Injectable } from 'angular-ts-decorators' ; import autobind from 'autobind-decorator' ; import { BaseError , ClientDataNotFoundError , DailyNewSyncLimitReachedError , DataOutOfSyncError , HttpRequestAbortedError , HttpRequestFailedError , HttpRequestTimedOutError , InvalidServiceError , NetworkConnectionError , NotAcceptingNewSyncsError , RequestEntityTooLargeError , ServiceOfflineError , SyncNotFoundError , TooManyRequestsError , UnexpectedResponseDataError , UnsupportedApiVersionError } from '../../errors/errors' ; import Globals from '../../global-shared.constants' ; import { PlatformService } from '../../global-shared.interface' ; import { NetworkService } from '../../network/network.service' ; import { StoreKey } from '../../store/store.enum' ; import { StoreService } from '../../store/store.service' ; import { UtilityService } from '../../utility/utility.service' ; import { ApiCreateBookmarksRequest , ApiCreateBookmarksResponse , ApiGetBookmarksResponse , ApiGetLastUpdatedResponse , ApiGetSyncVersionResponse , ApiService , ApiServiceInfoResponse , ApiUpdateBookmarksRequest , ApiUpdateBookmarksResponse } from '../api.interface' ; import { ApiXbrowsersyncResource } from './api-xbrowsersync.enum' ; import { ApiXbrowsersyncErrorResponse , ApiXbrowsersyncServiceInfoResponse } from './api-xbrowsersync.interface' ; @ autobind @ Injectable ( 'ApiService' ) export class ApiXbrowsersyncService implements ApiService { $injector : ng . auto . IInjectorService ; $http : ng . IHttpService ; $q : ng . IQService ; networkSvc : NetworkService ; _platformSvc : PlatformService | undefined ; storeSvc : StoreService ; utilitySvc : UtilityService ; static $inject = [ '$injector' , '$http' , '$q' , 'NetworkService' , 'StoreService' , 'UtilityService' ] ; constructor ( $injector , $http , $q , NetworkSvc , StoreSvc , UtilitySvc ) { this . $injector = $injector ; this . $http = $http ; this . $q = $q ; this . networkSvc = NetworkSvc ; this . storeSvc = StoreSvc ; this . utilitySvc = UtilitySvc ; } get platformSvc ( ) : PlatformService { if ( angular . isUndefined ( this . _platformSvc ) ) { this . _platformSvc = this . $injector . get ( 'PlatformService' ) ; } return this . _platformSvc as PlatformService ; } apiRequestSucceeded < T > ( response ) { return this . $q . resolve ( response ) ; } checkNetworkConnection ( ) { return this . $q ( ( resolve , reject ) => { if ( this . networkSvc . isNetworkConnected ( ) ) { return resolve ( ) ; } reject ( new NetworkConnectionError ( ) ) ; } ) ; } checkServiceStatus ( url ? ) { return this . checkNetworkConnection ( ) . then ( ( ) => { return ( ! url ? this . utilitySvc . getServiceUrl ( ) : this . $q . resolve ( url ) ) . then ( ( serviceUrl ) => { const requestConfig = { method : 'GET' , url : `${ serviceUrl }/${ ApiXbrowsersyncResource . ServiceInformation }` , timeout : 3000 } ; return this . $http < ApiXbrowsersyncServiceInfoResponse > ( requestConfig ) . catch ( this . handleFailedRequest ) ; } ) . then ( this . apiRequestSucceeded ) . then ( ( response ) => { const { data : serviceInfo } = response ; if ( ! serviceInfo ?. status || ! serviceInfo ?. version ) { throw new InvalidServiceError ( ) ; } if ( this . utilitySvc . compareVersions ( serviceInfo . version , Globals . MinApiVersion , '<' ) ) { throw new UnsupportedApiVersionError ( ) ; } return serviceInfo ; } ) ; } ) ; } createNewSync ( ) { return this . checkNetworkConnection ( ) . then ( ( ) => { return this . $q . all ( [ this . platformSvc . getAppVersion ( ) , this . utilitySvc . getServiceUrl ( ) ] ) . then ( ( data ) => { const [ appVersion , serviceUrl ] = data ; const requestUrl = `${ serviceUrl }/${ ApiXbrowsersyncResource . Bookmarks }` ; const requestBody = { version : appVersion } ; return this . $http . post < ApiCreateBookmarksResponse > ( requestUrl , JSON . stringify ( requestBody ) ) . catch ( this . handleFailedRequest ) ; } ) . then ( this . apiRequestSucceeded ) . then ( ( response ) => { const { data } = response ; if ( ! data ?. id || ! data ?. lastUpdated || ! data ?. version ) { throw new UnexpectedResponseDataError ( ) ; } return data ; } ) ; } ) . catch ( ( err ) => { if ( err instanceof InvalidServiceError ) { throw new ServiceOfflineError ( ) ; } throw err ; } ) ; } getBookmarks ( ) { return this . storeSvc . get ( [ StoreKey . Password , StoreKey . SyncId ] ) . then ( ( storeContent ) => { if ( ! storeContent . password || ! storeContent . syncId ) { throw new ClientDataNotFoundError ( ) ; } return this . checkNetworkConnection ( ) . then ( ( ) => { return this . utilitySvc . getServiceUrl ( ) . then ( ( serviceUrl ) => { const requestUrl = `${ serviceUrl }/${ ApiXbrowsersyncResource . Bookmarks }/${ storeContent . syncId }` ; return this . $http . get < ApiGetBookmarksResponse > ( requestUrl ) . catch ( this . handleFailedRequest ) ; } ) . then ( this . apiRequestSucceeded ) . then ( ( response ) => { const { data } = response ; if ( ! data ?. lastUpdated ) { throw new UnexpectedResponseDataError ( ) ; } return data ; } ) ; } ) ; } ) . catch ( ( err ) => { if ( err instanceof InvalidServiceError ) { throw new ServiceOfflineError ( ) ; } throw err ; } ) ; } getBookmarksLastUpdated ( skipNetworkConnectionCheck = false ) { return this . storeSvc . get ( [ StoreKey . Password , StoreKey . SyncId ] ) . then ( ( storeContent ) => { if ( ! storeContent . password || ! storeContent . syncId ) { throw new ClientDataNotFoundError ( ) ; } return ( skipNetworkConnectionCheck ? this . $q . resolve ( ) : this . checkNetworkConnection ( ) ) . then ( ( ) => { return this . utilitySvc . getServiceUrl ( ) . then ( ( serviceUrl ) => { const requestUrl = `${ serviceUrl }/${ ApiXbrowsersyncResource . Bookmarks }/${ storeContent . syncId }/${ ApiXbrowsersyncResource . LastUpdated }` ; return this . $http . get < ApiGetLastUpdatedResponse > ( requestUrl ) . catch ( this . handleFailedRequest ) ; } ) . then ( this . apiRequestSucceeded ) . then ( ( response ) => { const { data } = response ; if ( ! data ?. lastUpdated ) { throw new UnexpectedResponseDataError ( ) ; } return data ; } ) ; } ) ; } ) . catch ( ( err ) => { if ( err instanceof InvalidServiceError ) { throw new ServiceOfflineError ( ) ; } throw err ; } ) ; } getBookmarksVersion ( syncId ) { return this . checkNetworkConnection ( ) . then ( ( ) => { return this . utilitySvc . getServiceUrl ( ) . then ( ( serviceUrl ) => { const requestUrl = `${ serviceUrl }/${ ApiXbrowsersyncResource . Bookmarks }/${ syncId }/${ ApiXbrowsersyncResource . Version }` ; return this . $http . get < ApiGetSyncVersionResponse > ( requestUrl ) . catch ( this . handleFailedRequest ) ; } ) . then ( this . apiRequestSucceeded ) . then ( ( response ) => { const { data : version } = response ; if ( ! version ) { throw new UnexpectedResponseDataError ( ) ; } return version ; } ) ; } ) . catch ( ( err ) => { if ( err instanceof InvalidServiceError ) { throw new ServiceOfflineError ( ) ; } throw err ; } ) ; } getErrorFromHttpResponse ( response ) { let error ; const message = response . data ?. message ; switch ( true ) { case response . status === 401 : error = new SyncNotFoundError ( message ) ; break ; case response . status === 404 : error = new InvalidServiceError ( message ) ; break ; case response . status === 405 : error = new NotAcceptingNewSyncsError ( message ) ; break ; case response . status === 406 : error = new DailyNewSyncLimitReachedError ( message ) ; break ; case response . status === 409 : error = new DataOutOfSyncError ( message ) ; break ; case response . status === 413 : error = new RequestEntityTooLargeError ( message ) ; break ; case response . status === 429 : error = new TooManyRequestsError ( message ) ; break ; case response . status >= 500 : error = new ServiceOfflineError ( message ) ; break ; case response . xhrStatus === 'timeout' : error = new HttpRequestTimedOutError ( ) ; break ; case response . xhrStatus === 'abort' : error = new HttpRequestAbortedError ( ) ; break ; default : error = new HttpRequestFailedError ( message ) ; } return error ; } handleFailedRequest ( response ) { throw this . getErrorFromHttpResponse ( response ) ; } updateBookmarks ( encryptedBookmarks , updateSyncVersion = false , skipNetworkConnectionCheck = false ) { return this . storeSvc . get ( [ StoreKey . LastUpdated , StoreKey . Password , StoreKey . SyncId ] ) . then ( ( storeContent ) => { if ( ! storeContent . lastUpdated || ! storeContent . password || ! storeContent . syncId ) { throw new ClientDataNotFoundError ( ) ; } return ( skipNetworkConnectionCheck ? this . $q . resolve ( ) : this . checkNetworkConnection ( ) ) . then ( ( ) => { return this . $q . all ( [ this . platformSvc . getAppVersion ( ) , this . utilitySvc . getServiceUrl ( ) ] ) . then ( ( data ) => { const [ appVersion , serviceUrl ] = data ; const requestUrl = `${ serviceUrl }/${ ApiXbrowsersyncResource . Bookmarks }/${ storeContent . syncId }` ; const requestBody = { bookmarks : encryptedBookmarks , lastUpdated : storeContent . lastUpdated } ; if ( updateSyncVersion ) { requestBody . version = appVersion ; } return this . $http . put < ApiUpdateBookmarksResponse > ( requestUrl , JSON . stringify ( requestBody ) ) . catch ( this . handleFailedRequest ) ; } ) . then ( this . apiRequestSucceeded ) . then ( ( response ) => { const { data } = response ; if ( ! data ?. lastUpdated ) { throw new UnexpectedResponseDataError ( ) ; } return data ; } ) ; } ) ; } ) . catch ( ( err ) => { if ( err instanceof InvalidServiceError ) { throw new ServiceOfflineError ( ) ; } throw err ; } ) ; } }