import {C8o, FullSyncPolicy} from "./c8o.service";
import {C8oResponseListener, C8oResponseJsonListener} from "./c8oResponse.service";
import {C8oUtils} from "./c8oUtils.service";
import {C8oException} from "./Exception/c8oException.service";
import {C8oExceptionMessage} from "./Exception/c8oExceptionMessage.service";
import {FullSyncRequestable} from "./fullSyncRequestable.service";
import {C8oFullSyncDatabase} from "./fullSyncDatabase.service";
import {FullSyncDocumentOperationResponse, FullSyncDefaultResponse} from "./fullSyncResponse.service";
import {C8oFullSyncTranslator} from "./c8oFullSyncTranslator.service";
import {C8oRessourceNotFoundException} from "./Exception/c8oRessourceNotFoundException.service";
import {C8oUnavailableLocalCacheException} from "./Exception/c8oUnavailableLocalCacheException.service";
import {C8oLocalCacheResponse} from "./c8oLocalCacheResponse.service";
import {FullSyncDeleteDocumentParameter} from "./fullSyncDeleteDocumentParameter.service";
import {C8oCouchBaseLiteException} from "./Exception/c8oCouchBaseLiteException.service";
import {C8oFullSyncChangeListener} from "./c8oFullSyncChangeListener.service";
export class C8oFullSync {
private static FULL_SYNC_URL_PATH: string = "/fullsync/";
/**
* The project requestable value to execute a fullSync request.
*/
public static FULL_SYNC_PROJECT: string = "fs://";
public static FULL_SYNC__ID: string = "_id";
public static FULL_SYNC__REV: string = "_rev";
public static FULL_SYNC__ATTACHMENTS: string = "_attachments";
c8o: C8o;
protected fullSyncDatabaseUrlBase: string;
protected localSuffix: string;
public constructor(c8o: C8o) {
this.c8o = c8o;
this.fullSyncDatabaseUrlBase = c8o.endpointConvertigo + C8oFullSync.FULL_SYNC_URL_PATH;
this.localSuffix = (c8o.fullSyncLocalSuffix !== null) ? c8o.fullSyncLocalSuffix : "_device";
}
/**
* Handles a fullSync request.
* It determines the type of the request thanks to parameters.
*
* @param _parameters
* @param listener
* @return promise
* @throws C8oException
*/
public handleFullSyncRequest(_parameters: Object, listener: C8oResponseListener): Promise {
let parameters = (JSON.parse(JSON.stringify(_parameters)));
let projectParameterValue: string = C8oUtils.peekParameterStringValue(parameters, C8o.ENGINE_PARAMETER_PROJECT, true);
if (!projectParameterValue.startsWith(C8oFullSync.FULL_SYNC_PROJECT)) {
throw new C8oException(C8oExceptionMessage.invalidParameterValue(projectParameterValue, "its don't start with" + C8oFullSync.FULL_SYNC_PROJECT));
}
let fullSyncRequestableValue: string = C8oUtils.peekParameterStringValue(parameters, C8o.ENGINE_PARAMETER_SEQUENCE, true);
// get rid of the optional trailing #RouteHint present in the sequence
if (fullSyncRequestableValue.indexOf("#") !== -1)
fullSyncRequestableValue = fullSyncRequestableValue.substring(0, fullSyncRequestableValue.indexOf("#"));
let fullSyncRequestable: FullSyncRequestable = FullSyncRequestable.getFullSyncRequestable(fullSyncRequestableValue);
if (fullSyncRequestable === null) {
throw new C8oException(C8oExceptionMessage.invalidParameterValue(C8o.ENGINE_PARAMETER_PROJECT, C8oExceptionMessage.unknownValue("fullSync requestable", fullSyncRequestableValue)));
}
let databaseName: string = projectParameterValue.substring(C8oFullSync.FULL_SYNC_PROJECT.length);
if (databaseName.length < 1) {
databaseName = this.c8o.defaultDatabaseName;
if (databaseName === null) {
throw new C8oException(C8oExceptionMessage.invalidParameterValue(C8o.ENGINE_PARAMETER_PROJECT, C8oExceptionMessage.missingValue("fullSync database name")));
}
}
let response: any;
return new Promise((resolve, reject) => {
fullSyncRequestable.handleFullSyncRequest(this, databaseName, parameters, listener).then((result) => {
response = result;
if (response === null || response === undefined) {
reject(new C8oException(C8oExceptionMessage.couchNullResult()));
}
resolve(this.handleFullSyncResponse(response, listener));
}).catch((error) => {
if (error instanceof C8oException) {
reject(error);
}
else {
reject(new C8oException(C8oExceptionMessage.FullSyncRequestFail(), error));
}
});
});
}
//noinspection JSUnusedLocalSymbols
/**
*
* @param response
* @param listener
* @return response
* @throws C8oException Failed to parse response.
*/
handleFullSyncResponse(response: any, listener: C8oResponseListener): any {
return response;
}
/**
* Checks if request parameters correspond to a fullSync request.
*/
static isFullSyncRequest(requestParameter: Object): boolean {
if (C8oUtils.getParameterStringValue(requestParameter, C8o.ENGINE_PARAMETER_PROJECT, false) !== null) {
return C8oUtils.getParameterStringValue(requestParameter, C8o.ENGINE_PARAMETER_PROJECT, false).startsWith(C8oFullSync.FULL_SYNC_PROJECT);
}
else {
return false;
}
}
}
export class C8oFullSyncCbl extends C8oFullSync {
private static ATTACHMENT_PROPERTY_KEY_CONTENT_URL: string = "content_url";
private fullSyncDatabases: Object;
private fullSyncChangeListeners: Array> = [];
private cblChangeListeners: Array = [];
constructor(c8o: C8o) {
super(c8o);
this.fullSyncDatabases = {};
}
/**
* Returns the database with this name in the list.
* If it does not already exist yet then creates it and adds it to the list.
*
* @param databaseName
* @return C8oFullSyncDatabase
* @throws C8oException Failed to create a new fullSync database.
*/
public getOrCreateFullSyncDatabase(databaseName: string): C8oFullSyncDatabase {
let localDatabaseName: string = databaseName + this.localSuffix;
if (this.fullSyncDatabases[localDatabaseName] == null) {
this.fullSyncDatabases[localDatabaseName] = new C8oFullSyncDatabase(this.c8o, databaseName, this.fullSyncDatabaseUrlBase, this.localSuffix);
}
return this.fullSyncDatabases[localDatabaseName];
}
handleFullSyncResponse(response: any, listener: C8oResponseListener): any {
response = super.handleFullSyncResponse(response, listener);
if (listener instanceof C8oResponseJsonListener) {
if (response instanceof FullSyncDocumentOperationResponse) {
return C8oFullSyncTranslator.fullSyncDocumentOperationResponseToJson(response as FullSyncDocumentOperationResponse);
}
else if (response instanceof FullSyncDefaultResponse) {
return C8oFullSyncTranslator.fullSyncDefaultResponseToJson(response as FullSyncDefaultResponse);
}
else if (response instanceof Object) {
return response as JSON;
}
else {
throw new C8oException(C8oExceptionMessage.illegalArgumentIncompatibleListener(listener.toString(), typeof response));
}
}
}
// DONE class C8oFullSyncCbl: function handleGetAttachmentUrlRequest
handleGetAttachmentUrlRequest(fullSyncDatabaseName: string, docid: string, parameters: Object): Promise {
let fullSyncDatabase: C8oFullSyncDatabase = null;
fullSyncDatabase = this.getOrCreateFullSyncDatabase(fullSyncDatabaseName);
let attachmentName = C8oUtils.getParameterStringValue(parameters, "attachment_name", false);
return new Promise((resolve) => {
fullSyncDatabase.getdatabase.getAttachment(docid, attachmentName).then((buffer) => {
resolve(buffer);
});
});
}
// DONE class C8oFullSyncCbl: function handleGetDocumentRequest
handleGetDocumentRequest(fullSyncDatabaseName: string, docid: string, parameters: Object): Promise {
let fullSyncDatabase: C8oFullSyncDatabase = null;
let dictDoc: Object = {};
let param: Object;
param = parameters["attachments"] ? {"attachments": true} : {};
parameters["binary"] ? param["binary"] = true : {};
fullSyncDatabase = this.getOrCreateFullSyncDatabase(fullSyncDatabaseName);
return new Promise((resolve, reject) => {
fullSyncDatabase.getdatabase.get(docid, param).then((document) => {
if (document != null) {
dictDoc = document;
let attachments = document[C8oFullSync.FULL_SYNC__ATTACHMENTS];
if (attachments != null) {
for (let attachmentName of attachments) {
let url = attachments["url"];
let attachmentDesc: Object = attachments[attachmentName];
attachmentDesc[C8oFullSyncCbl.ATTACHMENT_PROPERTY_KEY_CONTENT_URL] = url.toString();
let dictAny: Object = {};
dictAny[attachmentName] = attachmentDesc;
dictDoc[C8oFullSyncCbl.FULL_SYNC__ATTACHMENTS] = dictAny;
}
}
}
else {
throw new C8oRessourceNotFoundException((C8oExceptionMessage.ressourceNotFound("requested document \"" + docid + "\"")));
}
if (dictDoc === null) {
dictDoc = {};
}
resolve(dictDoc);
})
.catch((error) => {
reject(error);
});
});
}
// DONE class C8oFullSyncCbl: function handleDeleteDocumentRequest
handleDeleteDocumentRequest(DatabaseName: string, docid: string, parameters: Object): Promise {
let fullSyncDatabase: C8oFullSyncDatabase = null;
fullSyncDatabase = this.getOrCreateFullSyncDatabase(DatabaseName);
let revParameterValue: string = C8oUtils.getParameterStringValue(parameters, FullSyncDeleteDocumentParameter.REV.name, false);
let documentRevision: string;
if (revParameterValue === null) {
return new Promise((resolve, reject) => {
fullSyncDatabase.getdatabase.get(docid).then((doc) => {
if (doc === null) {
throw new C8oRessourceNotFoundException(C8oExceptionMessage.toDo());
}
documentRevision = doc._rev;
return fullSyncDatabase.getdatabase.remove(doc);
}).then((result) => {
resolve(new FullSyncDocumentOperationResponse(docid, documentRevision, result.ok));
}).catch((err) => {
reject(new C8oException(C8oExceptionMessage.couchRequestDeleteDocument(), err));
});
});
}
else {
return new Promise((resolve, reject) => {
fullSyncDatabase.getdatabase.remove(docid, revParameterValue)
.then((result) => {
resolve(new FullSyncDocumentOperationResponse(docid, documentRevision, result.ok));
}).catch((err) => {
reject(new C8oException(C8oExceptionMessage.couchRequestDeleteDocument(), err));
});
});
}
}
// DONE class C8oFullSyncCbl: function handlePostDocumentRequest
handlePostDocumentRequest(databaseName: string, fullSyncPolicy: FullSyncPolicy, parameters: Object): Promise {
let fullSyncDatabase: C8oFullSyncDatabase;
fullSyncDatabase = this.getOrCreateFullSyncDatabase(databaseName);
let subkeySeparatorParameterValue: string = C8oUtils.getParameterStringValue(parameters, C8o.FS_SUBKEY_SEPARATOR, false);
if (subkeySeparatorParameterValue == null) {
subkeySeparatorParameterValue = ".";
}
let newProperties = {};
for (let i = 0; i < Object.keys(parameters).length; i++) {
let parameterName: string = Object.keys(parameters)[i];
if (!parameterName.startsWith("__") && !parameterName.startsWith("_use_")) {
let objectParameterValue: any = parameters[Object.keys(parameters)[i]];
let paths: Array = parameterName.split(subkeySeparatorParameterValue);
if (paths.length > 1) {
parameterName = paths[0];
let count = paths.length - 1;
while (count > 0) {
let tmpObject: Object = {};
tmpObject[paths[count]] = objectParameterValue;
objectParameterValue = tmpObject;
count--;
}
let existProperty = newProperties[parameterName];
if (existProperty != null) {
C8oFullSyncCbl.mergeProperties(objectParameterValue, existProperty);
}
}
newProperties[parameterName] = objectParameterValue;
}
}
let db = fullSyncDatabase.getdatabase;
return new Promise((resolve, reject) => {
fullSyncPolicy.action(db, newProperties).then((createdDocument) => {
let fsDocOpeResp: FullSyncDocumentOperationResponse = new FullSyncDocumentOperationResponse(createdDocument.id, createdDocument.rev, createdDocument.ok);
resolve(fsDocOpeResp);
}).catch((error) => {
reject(error);
});
});
}
// DONE class C8oFullSyncCbl: function handlePutAttachmentRequest
handlePutAttachmentRequest(databaseName: string, docid: string, attachmentName: string, attachmentType: string, attachmentContent: MSStream): Promise {
let document: any = null;
let fullSyncDatabase: C8oFullSyncDatabase = this.getOrCreateFullSyncDatabase(databaseName);
return new Promise((resolve) => {
fullSyncDatabase.getdatabase.get(docid).then((result) => {
document = result;
if (document !== null) {
fullSyncDatabase.getdatabase.putAttachment(docid, attachmentName, attachmentContent, attachmentType)
.then((result) => {
// handle result
resolve(new FullSyncDocumentOperationResponse(result._id, result._rev, result.ok));
}).catch((err) => {
throw new C8oCouchBaseLiteException("Unable to put the attachment " + attachmentName + " to the document " + docid + ".", err);
});
} else {
throw new C8oRessourceNotFoundException(C8oExceptionMessage.toDo());
}
});
});
}
// DONE class C8oFullSyncCbl: function handleDeleteAttachmentRequest
handleDeleteAttachmentRequest(databaseName: string, docid: string, attachmentName: string): Promise {
let document: any = null;
let fullSyncDatabase: C8oFullSyncDatabase = this.getOrCreateFullSyncDatabase(databaseName);
return new Promise((resolve) => {
fullSyncDatabase.getdatabase.get(docid).then((result) => {
document = result;
}).then(() => {
if (document !== null) {
fullSyncDatabase.getdatabase.removeAttachment(docid, attachmentName, document._rev).catch((err) => {
throw new C8oCouchBaseLiteException("Unable to delete the attachment " + attachmentName + " to the document " + docid + ".", err);
});
}
else {
throw new C8oRessourceNotFoundException(C8oExceptionMessage.toDo());
}
resolve(new FullSyncDocumentOperationResponse(document._id, document._rev, true));
});
});
}
// DONE class C8oFullSyncCbl: function handleAllDocumentsRequest
public handleAllDocumentsRequest(databaseName: string, parameters: Object): Promise {
let fullSyncDatabase = null;
fullSyncDatabase = this.getOrCreateFullSyncDatabase(databaseName);
return new Promise((resolve, reject) => {
fullSyncDatabase.getdatabase
.allDocs(parameters)
.then((res) => {
resolve(res);
})
.catch(() => {
reject(new C8oException("TODO"));
});
});
}
// DONE class C8oFullSyncCbl: function handleGetViewRequest
handleGetViewRequest(databaseName: string, ddocName: string, viewName: string, parameters: Object): Promise {
let fullSyncDatabase = null;
fullSyncDatabase = this.getOrCreateFullSyncDatabase(databaseName);
let attachments;
let binary;
let include_docs;
if (parameters["attachments"] && window["cblite"] !== undefined) {
attachments = C8oUtils.getParameterStringValue(parameters, "attachments", false);
binary = C8oUtils.getParameterStringValue(parameters, "binary", false);
include_docs = C8oUtils.getParameterStringValue(parameters, "include_docs", false);
}
return new Promise((resolve, reject) => {
fullSyncDatabase.getdatabase.query(ddocName + "/" + viewName, parameters)
.then((result) => {
if (attachments) {
let array: Array