//*** Local cache ***// public override async Task <C8oLocalCacheResponse> GetResponseFromLocalCache(string c8oCallRequestIdentifier) { C8oFullSyncDatabase fullSyncDatabase = await GetOrCreateFullSyncDatabase(C8o.LOCAL_CACHE_DATABASE_NAME); Document localCacheDocument = fullSyncDatabase.Database.GetExistingDocument(c8oCallRequestIdentifier); if (localCacheDocument == null) { throw new C8oUnavailableLocalCacheException(C8oExceptionMessage.MissingLocalCacheResponseDocument()); } IDictionary <string, object> properties = localCacheDocument.Properties; string response; string responseType; long expirationDate; if (!C8oUtils.TryGetParameterObjectValue <String>(properties, C8o.LOCAL_CACHE_DOCUMENT_KEY_RESPONSE, out response) || !C8oUtils.TryGetParameterObjectValue <String>(properties, C8o.LOCAL_CACHE_DOCUMENT_KEY_RESPONSE_TYPE, out responseType)) { throw new C8oUnavailableLocalCacheException(C8oExceptionMessage.InvalidLocalCacheResponseInformation()); } if (!C8oUtils.TryGetParameterObjectValue <long>(properties, C8o.LOCAL_CACHE_DOCUMENT_KEY_EXPIRATION_DATE, out expirationDate)) { expirationDate = -1; } return(new C8oLocalCacheResponse(response, responseType, expirationDate)); }
//*** DeleteDocument ***// /// <summary> /// Deletes an existing document from the local database. /// </summary> /// <param name="fullSyncDatabase"></param> /// <param name="parameters"></param> /// <returns></returns> public async override Task <object> HandleDeleteDocumentRequest(string fullSyncDatatbaseName, string docid, IDictionary <string, object> parameters) { var fullSyncDatabase = await GetOrCreateFullSyncDatabase(fullSyncDatatbaseName); string revParameterValue = C8oUtils.GetParameterStringValue(parameters, FullSyncDeleteDocumentParameter.REV.name, false); var document = fullSyncDatabase.Database.GetExistingDocument(docid); if (document == null) { throw new C8oRessourceNotFoundException(C8oExceptionMessage.RessourceNotFound("requested document")); } string documentRevision = document.CurrentRevisionId; // If the revision is specified then checks if this is the right revision if (revParameterValue != null && !revParameterValue.Equals(documentRevision)) { throw new C8oRessourceNotFoundException(C8oExceptionMessage.RessourceNotFound("requested document")); } try { document.Delete(); } catch (Exception e) { throw new C8oException(C8oExceptionMessage.CouchDeleteFailed(), e); } bool deleted = document.Deleted; return(new FullSyncDocumentOperationResponse(docid, revParameterValue, deleted)); }
//*** GetDocument ***// /// <summary> /// Returns the requested document. /// </summary> /// <param name="fullSyncDatatbase"></param> /// <param name="parameters"></param> /// <returns></returns> public async override Task <object> HandleGetDocumentRequest(string fullSyncDatatbaseName, string docid, IDictionary <string, object> parameters) { var fullSyncDatabase = await GetOrCreateFullSyncDatabase(fullSyncDatatbaseName); // Gets the document form the local database var document = fullSyncDatabase.Database.GetExistingDocument(docid); if (document != null) { // If there are attachments, compute for each one the url to local storage and add it to the attachment descriptor var attachmentsProperty = document.GetProperty(C8oFullSync.FULL_SYNC__ATTACHMENTS) as JObject; if (attachmentsProperty != null) { SavedRevision rev = document.CurrentRevision; Assembly couchbaseLiteAssembly = Assembly.GetAssembly(typeof(Attachment)); Type attachmentInternalType = couchbaseLiteAssembly.GetType(ATTACHMENT_INTERNAL_TYPE); ConstructorInfo attachmentInternalConstructor = attachmentInternalType.GetConstructor(new Type[] { typeof(String), typeof(IDictionary <string, object>) }); foreach (var attachmentProperty in attachmentsProperty) { string attachmentName = attachmentProperty.Key; Attachment attachment = rev.GetAttachment(attachmentName); if (!attachment.Metadata.Keys.Contains(ATTACHMENT_PROPERTY_KEY_CONTENT_URL)) { Object[] attachmentInternalConstructorParams = new Object[] { attachment.Name, attachment.Metadata }; object attachmentInternal = attachmentInternalConstructor.Invoke(attachmentInternalConstructorParams); PropertyInfo databaseProp = attachmentInternalType.GetProperty(ATTACHMENT_INTERNAL_PROPERTY_DATABASE); databaseProp.SetValue(attachmentInternal, fullSyncDatabase.Database); PropertyInfo urlProp = attachmentInternalType.GetProperty(ATTACHMENT_INTERNAL_PROPERTY_CONTENT_URL); object contentUrl = urlProp.GetValue(attachmentInternal, null); if (contentUrl != null && contentUrl is Uri) { Uri uri = (Uri)contentUrl; string absoluteUri = C8oUtils.UrlDecode(uri.AbsoluteUri); string absolutePath = C8oUtils.UrlDecode(uri.AbsolutePath); attachment.Metadata.Add(ATTACHMENT_PROPERTY_KEY_CONTENT_URL, absoluteUri); if (attachmentProperty.Value is JObject) { (attachmentProperty.Value as JObject)[ATTACHMENT_PROPERTY_KEY_CONTENT_URL] = absoluteUri; } attachment.Metadata.Add("content_path", absolutePath); if (attachmentProperty.Value is JObject) { (attachmentProperty.Value as JObject)["content_path"] = absolutePath; } } } } } } else { throw new C8oRessourceNotFoundException(C8oExceptionMessage.RessourceNotFound("requested document \"" + docid + "\"")); } return(document); }
//*** Request handlers ***// public async Task <object> HandleFullSyncRequest(IDictionary <string, object> parameters, C8oResponseListener listener) { parameters = new Dictionary <string, object>(parameters); // Gets the project and the sequence parameter in order to know which database and which fullSyncrequestable to use string projectParameterValue = C8oUtils.PeekParameterStringValue(parameters, C8o.ENGINE_PARAMETER_PROJECT, true); if (!projectParameterValue.StartsWith(FULL_SYNC_PROJECT)) { throw new ArgumentException(C8oExceptionMessage.InvalidParameterValue(projectParameterValue, "its don't start with " + FULL_SYNC_PROJECT)); } string fullSyncRequestableValue = C8oUtils.PeekParameterStringValue(parameters, C8o.ENGINE_PARAMETER_SEQUENCE, true); // Gets the fullSync requestable and gets the response from this requestable FullSyncRequestable fullSyncRequestable = FullSyncRequestable.GetFullSyncRequestable(fullSyncRequestableValue); if (fullSyncRequestable == null) { throw new ArgumentException(C8oExceptionMessage.InvalidParameterValue(C8o.ENGINE_PARAMETER_PROJECT, C8oExceptionMessage.UnknownValue("fullSync requestable", fullSyncRequestableValue))); } // Gets the database name if this is not specified then if takes the default database name string databaseName = projectParameterValue.Substring(C8oFullSync.FULL_SYNC_PROJECT.Length); if (databaseName.Length < 1) { databaseName = c8o.DefaultDatabaseName; if (databaseName == null) { throw new ArgumentException(C8oExceptionMessage.InvalidParameterValue(C8o.ENGINE_PARAMETER_PROJECT, C8oExceptionMessage.MissingValue("fullSync database name"))); } } Object response; try { response = await fullSyncRequestable.HandleFullSyncRequest(this, databaseName, parameters, listener); } catch (C8oException e) { throw e; } catch (Exception e) { throw new C8oException(C8oExceptionMessage.FullSyncRequestFail(), e); } if (response == null) { throw new C8oException(C8oExceptionMessage.couchNullResult()); } response = HandleFullSyncResponse(response, listener); return(response); }
public static object GetParameterJsonValue(IDictionary <string, object> parameters, string name, bool useName = false) { var parameter = GetParameter(parameters, name, useName); if (parameter.Key != null) { return(C8oUtils.GetParameterJsonValue(parameter)); } return(null); }
/// <summary> /// Checks if request parameters correspond to a fullSync request. /// </summary> public static bool IsFullSyncRequest(IDictionary <string, object> requestParameters) { // Check if there is one parameter named "__project" and if its value starts with "fs://" string parameterValue = C8oUtils.GetParameterStringValue(requestParameters, C8o.ENGINE_PARAMETER_PROJECT, false); if (parameterValue != null) { return(parameterValue.StartsWith(FULL_SYNC_PROJECT)); } return(false); }
//public override void SaveResponseToLocalCache(string c8oCallRequestIdentifier, string responseString, string responseType, int localCacheTimeToLive) //{ // Dictionary<string, object> properties = new Dictionary<string, object>(); // properties[C8o.LOCAL_CACHE_DOCUMENT_KEY_RESPONSE] = responseString; // properties[C8o.LOCAL_CACHE_DOCUMENT_KEY_RESPONSE_TYPE] = responseType; // if (localCacheTimeToLive != null) // { // long expirationDate = (long) C8oUtils.GetUnixEpochTime(DateTime.Now) + localCacheTimeToLive; // properties[C8o.LOCAL_CACHE_DOCUMENT_KEY_EXPIRATION_DATE] = expirationDate; // } // handlePostDocumentRequest(C8o.LOCAL_CACHE_DATABASE_NAME, FullSyncPolicy.OVERRIDE, properties); //} private async Task <IDictionary <string, object> > HandleRev(string fullSyncDatatbaseName, string docid, IDictionary <string, object> parameters) { var parameter = C8oUtils.GetParameter(parameters, FullSyncDeleteDocumentParameter.REV.name, false); if (parameter.Key == null) { string rev = await GetDocumentRev(fullSyncDatatbaseName, docid); if (rev != null) { parameters[FullSyncDeleteDocumentParameter.REV.name] = rev; } } return(parameters); }
//*** PostDocument ***// public async override Task <object> HandlePostDocumentRequest(string databaseName, FullSyncPolicy fullSyncPolicy, IDictionary <string, object> parameters) { var fullSyncDatabase = await GetOrCreateFullSyncDatabase(databaseName); // Gets the subkey separator parameter string subkeySeparatorParameterValue = C8oUtils.GetParameterStringValue(parameters, FullSyncPostDocumentParameter.SUBKEY_SEPARATOR.name, false); if (subkeySeparatorParameterValue == null) { subkeySeparatorParameterValue = "."; } // Filters and modifies wrong properties var newProperties = new Dictionary <string, object>(); foreach (var parameter in parameters) { string parameterName = parameter.Key; if (parameterName.Equals(C8oFullSync.FULL_SYNC__REV)) { newProperties[parameterName] = parameter.Value; } else if (!parameterName.StartsWith("__") && !parameterName.StartsWith("_use_")) { // Retrieves ??? var objectParameterValue = C8oUtils.GetParameterJsonValue(parameter); //Manager.SharedInstance. ow = null; if (objectParameterValue is JObject) { objectParameterValue = (objectParameterValue as JObject).ToObject <Dictionary <string, object> > (); } // Checks if the parameter name is splittable var paths = parameterName.Split(new String[] { subkeySeparatorParameterValue }, StringSplitOptions.None); // Regex.Split(parameterName, subkeySeparatorParameterValue); if (paths.Length > 1) { // The first substring becomes the key parameterName = paths[0]; // Next substrings create a hierarchy which will becomes json subkeys int count = paths.Length - 1; while (count > 0) { var tmpObject = new Dictionary <string, object>(); tmpObject[paths[count]] = objectParameterValue; objectParameterValue = tmpObject; count--; } if (newProperties.ContainsKey(parameterName) && newProperties[parameterName] is IDictionary <string, object> ) { FullSyncUtils.MergeProperties(objectParameterValue as IDictionary <string, object>, newProperties[parameterName] as IDictionary <string, object>); } } newProperties[parameterName] = objectParameterValue; } } var createdDocument = C8oFullSyncCblEnum.PostDocument(fullSyncPolicy, fullSyncDatabase.Database, newProperties); string documentId = createdDocument.Id; string currentRevision = createdDocument.CurrentRevisionId; return(new FullSyncDocumentOperationResponse(documentId, currentRevision, true)); }
public async override Task <object> HandlePostDocumentRequest(string fullSyncDatatbaseName, FullSyncPolicy fullSyncPolicy, IDictionary <string, object> parameters) { await CheckDatabase(fullSyncDatatbaseName); var options = new Dictionary <string, object>(); foreach (var parameter in parameters) { var isUse = RE_FS_USE.Match(parameter.Key); if (isUse.Success) { if (isUse.Groups[1].Success) { options[isUse.Groups[1].Value] = parameter.Value; } parameters.Remove(parameter.Key); } } string uri = HandleQuery(GetDatabaseUrl(fullSyncDatatbaseName), options); var request = HttpWebRequest.CreateHttp(uri); request.Method = "POST"; // Gets the subkey separator parameter string subkeySeparatorParameterValue = C8oUtils.PeekParameterStringValue(parameters, FullSyncPostDocumentParameter.SUBKEY_SEPARATOR.name, false); if (subkeySeparatorParameterValue == null) { subkeySeparatorParameterValue = "."; } var postData = new JObject(); foreach (KeyValuePair <string, object> kvp in parameters) { var obj = postData; string key = kvp.Key; String[] paths = key.Split(subkeySeparatorParameterValue.ToCharArray()); if (paths.Length > 1) { for (int i = 0; i < paths.Length - 1; i++) { string path = paths[i]; if (obj[path] is JObject) { obj = obj[path] as JObject; } else { obj = (obj[path] = new JObject()) as JObject; } } key = paths[paths.Length - 1]; } obj[key] = JToken.FromObject(kvp.Value); } postData = await ApplyPolicy(fullSyncDatatbaseName, postData, fullSyncPolicy); return(await Execute(request, postData)); }
async private Task <object> HandleRequest() { bool isFullSyncRequest = C8oFullSync.IsFullSyncRequest(parameters); if (isFullSyncRequest) { c8o.Log._Debug("Is FullSync request"); var liveid = C8oUtils.GetParameterStringValue(parameters, C8o.FS_LIVE, false); if (liveid != null) { var dbName = C8oUtils.GetParameterStringValue(parameters, C8o.ENGINE_PARAMETER_PROJECT, true).Substring(C8oFullSync.FULL_SYNC_PROJECT.Length); c8o.AddLive(liveid, dbName, this); } // The result cannot be handled here because it can be different depending to the platform // But it can be useful bor debug try { var fullSyncResult = await c8o.c8oFullSync.HandleFullSyncRequest(parameters, c8oResponseListener); return(fullSyncResult); } catch (C8oException e) { throw e; } catch (Exception e) { throw new C8oException(C8oExceptionMessage.FullSyncRequestFail(), e); } } else { string responseType; if (c8oResponseListener == null || c8oResponseListener is C8oResponseXmlListener) { responseType = C8o.RESPONSE_TYPE_XML; } else if (c8oResponseListener is C8oResponseJsonListener) { responseType = C8o.RESPONSE_TYPE_JSON; } else { return(new C8oException("wrong listener")); } //*** Local cache ***// string c8oCallRequestIdentifier = null; // Allows to enable or disable the local cache on a Convertigo requestable, default value is true C8oLocalCache localCache = C8oUtils.GetParameterObjectValue(parameters, C8oLocalCache.PARAM, false) as C8oLocalCache; bool localCacheEnabled = false; // If the engine parameter for local cache is specified if (localCache != null) { // Removes local cache parameters and build the c8o call request identifier parameters.Remove(C8oLocalCache.PARAM); if (localCacheEnabled = localCache.enabled) { c8oCallRequestIdentifier = C8oUtils.IdentifyC8oCallRequest(parameters, responseType); if (localCache.priority.IsAvailable(c8o)) { try { C8oLocalCacheResponse localCacheResponse = await c8o.c8oFullSync.GetResponseFromLocalCache(c8oCallRequestIdentifier); if (!localCacheResponse.Expired) { if (responseType == C8o.RESPONSE_TYPE_XML) { return(C8oTranslator.StringToXml(localCacheResponse.Response)); } else if (responseType == C8o.RESPONSE_TYPE_JSON) { return(C8oTranslator.StringToJson(localCacheResponse.Response)); } } } catch (C8oUnavailableLocalCacheException) { // no entry } } } } //*** Get response ***// parameters[C8o.ENGINE_PARAMETER_DEVICE_UUID] = c8o.DeviceUUID; // Build the c8o call URL c8oCallUrl = c8o.Endpoint + "/." + responseType; HttpWebResponse httpResponse = null; Exception exception = null; try { httpResponse = await c8o.httpInterface.HandleC8oCallRequest(c8oCallUrl, parameters); } catch (Exception e) { exception = e; } if (exception != null) { if (localCacheEnabled) { try { C8oLocalCacheResponse localCacheResponse = await c8o.c8oFullSync.GetResponseFromLocalCache(c8oCallRequestIdentifier); if (!localCacheResponse.Expired) { if (responseType == C8o.RESPONSE_TYPE_XML) { return(C8oTranslator.StringToXml(localCacheResponse.Response)); } else if (responseType == C8o.RESPONSE_TYPE_JSON) { return(C8oTranslator.StringToJson(localCacheResponse.Response)); } } } catch (C8oUnavailableLocalCacheException) { // no entry } } return(new C8oException(C8oExceptionMessage.handleC8oCallRequest(), exception)); } var responseStream = httpResponse.GetResponseStream(); object response; string responseString = null; if (c8oResponseListener is C8oResponseXmlListener) { response = C8oTranslator.StreamToXml(responseStream); if (localCacheEnabled) { responseString = C8oTranslator.XmlToString(response as XDocument); } } else if (c8oResponseListener is C8oResponseJsonListener) { responseString = C8oTranslator.StreamToString(responseStream); response = C8oTranslator.StringToJson(responseString); } else { return(new C8oException("wrong listener")); } if (localCacheEnabled) { // String responseString = C8oTranslator.StreamToString(responseStream); long expirationDate = -1; if (localCache.ttl > 0) { expirationDate = localCache.ttl + C8oUtils.GetUnixEpochTime(DateTime.Now); } var localCacheResponse = new C8oLocalCacheResponse(responseString, responseType, expirationDate); await c8o.c8oFullSync.SaveResponseToLocalCache(c8oCallRequestIdentifier, localCacheResponse); } return(response); } }
//*** FullSyncPolicies ***// private static void InitFullSyncPolicies() { fullSyncPolicies = new Dictionary <FullSyncPolicy, Func <Database, IDictionary <string, object>, Document> >(); FullSyncPolicy policy; Func <Database, IDictionary <string, object>, Document> func; // NONE policy = FullSyncPolicy.NONE; func = (database, newProperties) => { Document createdDocument; try { string documentId = C8oUtils.GetParameterStringValue(newProperties, C8oFullSync.FULL_SYNC__ID, false); // removes special properties newProperties.Remove(C8oFullSync.FULL_SYNC__ID); // Creates a new document or get an existing one (if the ID is specified) createdDocument = (documentId == null) ? database.CreateDocument() : database.GetDocument(documentId); createdDocument.PutProperties(newProperties); } catch (CouchbaseLiteException e) { throw new C8oCouchbaseLiteException(C8oExceptionMessage.FullSyncPutProperties(newProperties), e); } return(createdDocument); }; fullSyncPolicies.Add(policy, func); // CREATE policy = FullSyncPolicy.CREATE; func = (database, newProperties) => { Document createdDocument; try { // Removes specials properties in order to create a new document newProperties.Remove(C8oFullSync.FULL_SYNC__ID); newProperties.Remove(C8oFullSync.FULL_SYNC__REV); createdDocument = database.CreateDocument(); createdDocument.PutProperties(newProperties); } catch (CouchbaseLiteException e) { throw new C8oCouchbaseLiteException(C8oExceptionMessage.FullSyncPutProperties(newProperties), e); } return(createdDocument); }; fullSyncPolicies.Add(policy, func); // OVERRIDE policy = FullSyncPolicy.OVERRIDE; func = (database, newProperties) => { Document createdDocument; try { // Gets the document ID string documentId = C8oUtils.GetParameterStringValue(newProperties, C8oFullSync.FULL_SYNC__ID, false); // Removes special properties in order to create a new document newProperties.Remove(C8oFullSync.FULL_SYNC__ID); newProperties.Remove(C8oFullSync.FULL_SYNC__REV); // Creates a new document or get an existing one (if the ID is specified) if (documentId == null) { createdDocument = database.CreateDocument(); } else { createdDocument = database.GetDocument(documentId); // Must add the current revision to the properties var currentRevision = createdDocument.CurrentRevision; if (currentRevision != null) { newProperties[C8oFullSync.FULL_SYNC__REV] = currentRevision.Id; } } createdDocument.PutProperties(newProperties); } catch (CouchbaseLiteException e) { throw new C8oCouchbaseLiteException(C8oExceptionMessage.FullSyncPutProperties(newProperties), e); } return(createdDocument); }; fullSyncPolicies.Add(policy, func); // MERGE policy = FullSyncPolicy.MERGE; func = (database, newProperties) => { Document createdDocument; try { // Gets the document ID string documentId = C8oUtils.GetParameterStringValue(newProperties, C8oFullSync.FULL_SYNC__ID, false); // Removes special properties in order to create a new document newProperties.Remove(C8oFullSync.FULL_SYNC__ID); newProperties.Remove(C8oFullSync.FULL_SYNC__REV); // Creates a new document or get an existing one (if the ID is specified) if (documentId == null) { createdDocument = database.CreateDocument(); } else { createdDocument = database.GetDocument(documentId); } // Merges old properties with the new ones var oldProperties = createdDocument.Properties; if (oldProperties != null) { FullSyncUtils.MergeProperties(newProperties, oldProperties); } createdDocument.PutProperties(newProperties); } catch (CouchbaseLiteException e) { throw new C8oCouchbaseLiteException(C8oExceptionMessage.FullSyncPutProperties(newProperties), e); } return(createdDocument); }; fullSyncPolicies.Add(policy, func); }