public HeadVersion(IDatabase db, Data.VersionId versionId, int sendTimeout, int receiveTimeout, int sendBufferSize, int receiveBufferSize) : base(sendTimeout, receiveTimeout, sendBufferSize, receiveBufferSize) { _db = db; _versionId = versionId; }
public override void CheckoutVersion(EngineRequest request, Data.VersionId version) { CheckInitialization(); Logger.Storage.Debug("Checking out the version '" + version.ToString() + "'"); EngineMethods.CheckoutVersion act = new EngineMethods.CheckoutVersion(request, version); act.Execute(); }
public DownloadVersion(IDatabase db, Data.VersionId id, int sendTimeout, int receiveTimeout, int sendBufferSize, int receiveBufferSize) : base(sendTimeout, receiveTimeout, sendBufferSize, receiveBufferSize) { _db = db; _id = id; }
public CheckoutVersion(IDatabase db, Data.VersionId id, Security.RequestingPartyType requestingPartyType, Security.Session session, int sendTimeout, int receiveTimeout, int sendBufferSize, int receiveBufferSize) : base(db, sendTimeout, receiveTimeout, sendBufferSize, receiveBufferSize) { _id = id; _requestingPartyType = requestingPartyType; _session = session; }
public Data.Version Transition(Model.Document document, out JObject remainder) { Data.Version version; Data.VersionId id; string rev; string verifyString; if (!VerifyDocumentIntegrity(document, out verifyString)) { Logger.Storage.Error("The document is not properly formatted. It is missing the property '" + verifyString + "'."); throw new FormattingException("The argument document does not have the necessary property '" + verifyString + "'."); } try { id = new Data.VersionId(document.Id); if (document["_rev"] != null) { rev = document.Rev; version = new Data.Version(id, rev); document.Remove("_rev"); } else { version = new Data.Version(id); } document.Remove("_id"); document.Remove("$type"); version.Md5 = document["$md5"].Value<string>(); version.Extension = document["$extension"].Value<string>(); version.Created = document["$created"].Value<DateTime>(); version.Creator = document["$creator"].Value<string>(); version.Modified = document["$modified"].Value<DateTime>(); version.Modifier = document["$modifier"].Value<string>(); document.Remove("$md5"); document.Remove("$extension"); document.Remove("$created"); document.Remove("$creator"); document.Remove("$modified"); document.Remove("$modifier"); } catch (Exception e) { Logger.Storage.Error("An exception occurred while attempting to parse the document.", e); throw; } remainder = document; return version; }
public Data.Version Transition(Model.Document document, out JObject remainder) { Data.Version version; Data.VersionId id; string rev; string verifyString; if (!VerifyDocumentIntegrity(document, out verifyString)) { Logger.Storage.Error("The document is not properly formatted. It is missing the property '" + verifyString + "'."); throw new FormattingException("The argument document does not have the necessary property '" + verifyString + "'."); } try { id = new Data.VersionId(document.Id); if (document["_rev"] != null) { rev = document.Rev; version = new Data.Version(id, rev); document.Remove("_rev"); } else { version = new Data.Version(id); } document.Remove("_id"); document.Remove("$type"); version.Md5 = document["$md5"].Value <string>(); version.Extension = document["$extension"].Value <string>(); version.Created = document["$created"].Value <DateTime>(); version.Creator = document["$creator"].Value <string>(); version.Modified = document["$modified"].Value <DateTime>(); version.Modifier = document["$modifier"].Value <string>(); document.Remove("$md5"); document.Remove("$extension"); document.Remove("$created"); document.Remove("$creator"); document.Remove("$modified"); document.Remove("$modifier"); } catch (Exception e) { Logger.Storage.Error("An exception occurred while attempting to parse the document.", e); throw; } remainder = document; return(version); }
public CheckoutVersion(EngineRequest request, Data.VersionId versionId) : base(request) { _versionId = versionId; }
public Data.Resource Transition(Model.Document document, out JObject remainder) { Data.Resource resource; Data.ResourceId id; string rev = null; Data.VersionId currentVersionId; List <Data.VersionId> versionIds = null; List <Security.UsageRight> usageRights = null; Security.UsageRight usageRight = null; JObject jobj = null; JProperty prop = null; IEnumerator <JToken> en; string verifyString; JArray jarray = new JArray(); if (!VerifyDocumentIntegrity(document, out verifyString)) { Logger.Storage.Error("The document is not properly formatted. It is missing the property '" + verifyString + "'."); throw new FormattingException("The argument document does not have the necessary property '" + verifyString + "'."); } // I ran into a problem here where I was removing the properties from the document and what was left was the remainder. // However, this causes an issue when using the transition to make a resource for permissions checking as the // object returned to implementing software is the document. Thus, the implementing software only received those properties // not removed... which obviously excludes the most important properties. To remedy this issue, I created a constructor for // Model.Document(Document). This constructor will format the argument document to a string and then create a JObject from // that string. C# will deep copy the string (not byref) so as to guarantee an independent object. remainder = new Model.Document(document); try { id = new Data.ResourceId(document.Id); if (document["_rev"] != null) { rev = document.Rev; remainder.Remove("_rev"); } remainder.Remove("_id"); remainder.Remove("$type"); currentVersionId = new Data.VersionId(document["$currentversionid"].Value <string>()); remainder.Remove("$currentversionid"); versionIds = new List <Data.VersionId>(); jarray = (JArray)document["$versionids"]; for (int i = 0; i < jarray.Count; i++) { versionIds.Add(new Data.VersionId(jarray[i].Value <string>())); } remainder.Remove("$versionids"); usageRights = new List <Security.UsageRight>(); jarray = (JArray)document["$usagerights"]; for (int i = 0; i < jarray.Count; i++) { jobj = (JObject)jarray[i]; en = jobj.Children().GetEnumerator(); while (en.MoveNext()) { prop = (JProperty)en.Current; // Json.Net is giving me some weird errors here when I try to call prop.value<int>(); // I cannot figure out why so this code is a temporary work-around, it needs figured out. string a = prop.ToString(); a = a.Substring(a.LastIndexOf("\"") + 1); // we know the value is an int, so we can look for the last " a = a.Replace(":", "").Trim(); usageRight = new Security.UsageRight(prop.Name, (Security.Authorization.ResourcePermissionType) int.Parse(a)); usageRights.Add(usageRight); } } remainder.Remove("$usagerights"); resource = new Data.Resource(id, rev, versionIds, currentVersionId, new Data.Metadata(), usageRights); // Tags resource.Tags = new List <string>(); jarray = (JArray)document["$tags"]; for (int i = 0; i < jarray.Count; i++) { resource.Tags.Add(jarray[i].Value <string>()); } remainder.Remove("$tags"); resource.Created = document["$created"].Value <DateTime>(); resource.Creator = document["$creator"].Value <string>(); resource.Modified = document["$modified"].Value <DateTime>(); resource.Modifier = document["$modifier"].Value <string>(); resource.CheckedOutAt = document["$checkedoutat"].Value <DateTime>(); resource.CheckedOutTo = document["$checkedoutto"].Value <string>(); resource.LastCommit = document["$lastcommit"].Value <DateTime>(); resource.LastCommitter = document["$lastcommitter"].Value <string>(); resource.Title = document["$title"].Value <string>(); remainder.Remove("$created"); remainder.Remove("$creator"); remainder.Remove("$modified"); remainder.Remove("$modifier"); remainder.Remove("$checkedoutat"); remainder.Remove("$checkedoutto"); remainder.Remove("$lastcommit"); remainder.Remove("$lastcommitter"); remainder.Remove("$title"); } catch (Exception e) { Logger.Storage.Error("An exception occurred while attempting to parse the document.", e); throw; } return(resource); }
public Data.Resource Transition(Model.Document document, out JObject remainder) { Data.Resource resource; Data.ResourceId id; string rev = null; Data.VersionId currentVersionId; List<Data.VersionId> versionIds = null; List<Security.UsageRight> usageRights = null; Security.UsageRight usageRight = null; JObject jobj = null; JProperty prop = null; IEnumerator<JToken> en; string verifyString; JArray jarray = new JArray(); if (!VerifyDocumentIntegrity(document, out verifyString)) { Logger.Storage.Error("The document is not properly formatted. It is missing the property '" + verifyString + "'."); throw new FormattingException("The argument document does not have the necessary property '" + verifyString + "'."); } // I ran into a problem here where I was removing the properties from the document and what was left was the remainder. // However, this causes an issue when using the transition to make a resource for permissions checking as the // object returned to implementing software is the document. Thus, the implementing software only received those properties // not removed... which obviously excludes the most important properties. To remedy this issue, I created a constructor for // Model.Document(Document). This constructor will format the argument document to a string and then create a JObject from // that string. C# will deep copy the string (not byref) so as to guarantee an independent object. remainder = new Model.Document(document); try { id = new Data.ResourceId(document.Id); if (document["_rev"] != null) { rev = document.Rev; remainder.Remove("_rev"); } remainder.Remove("_id"); remainder.Remove("$type"); currentVersionId = new Data.VersionId(document["$currentversionid"].Value<string>()); remainder.Remove("$currentversionid"); versionIds = new List<Data.VersionId>(); jarray = (JArray)document["$versionids"]; for (int i = 0; i < jarray.Count; i++) versionIds.Add(new Data.VersionId(jarray[i].Value<string>())); remainder.Remove("$versionids"); usageRights = new List<Security.UsageRight>(); jarray = (JArray)document["$usagerights"]; for (int i = 0; i < jarray.Count; i++) { jobj = (JObject)jarray[i]; en = jobj.Children().GetEnumerator(); while (en.MoveNext()) { prop = (JProperty)en.Current; // Json.Net is giving me some weird errors here when I try to call prop.value<int>(); // I cannot figure out why so this code is a temporary work-around, it needs figured out. string a = prop.ToString(); a = a.Substring(a.LastIndexOf("\"") + 1); // we know the value is an int, so we can look for the last " a = a.Replace(":", "").Trim(); usageRight = new Security.UsageRight(prop.Name, (Security.Authorization.ResourcePermissionType)int.Parse(a)); usageRights.Add(usageRight); } } remainder.Remove("$usagerights"); resource = new Data.Resource(id, rev, versionIds, currentVersionId, new Data.Metadata(), usageRights); // Tags resource.Tags = new List<string>(); jarray = (JArray)document["$tags"]; for (int i = 0; i < jarray.Count; i++) resource.Tags.Add(jarray[i].Value<string>()); remainder.Remove("$tags"); resource.Created = document["$created"].Value<DateTime>(); resource.Creator = document["$creator"].Value<string>(); resource.Modified = document["$modified"].Value<DateTime>(); resource.Modifier = document["$modifier"].Value<string>(); resource.CheckedOutAt = document["$checkedoutat"].Value<DateTime>(); resource.CheckedOutTo = document["$checkedoutto"].Value<string>(); resource.LastCommit = document["$lastcommit"].Value<DateTime>(); resource.LastCommitter = document["$lastcommitter"].Value<string>(); resource.Title = document["$title"].Value<string>(); remainder.Remove("$created"); remainder.Remove("$creator"); remainder.Remove("$modified"); remainder.Remove("$modifier"); remainder.Remove("$checkedoutat"); remainder.Remove("$checkedoutto"); remainder.Remove("$lastcommit"); remainder.Remove("$lastcommitter"); remainder.Remove("$title"); } catch (Exception e) { Logger.Storage.Error("An exception occurred while attempting to parse the document.", e); throw; } return resource; }
public override void TaskComplete(Tasks.Base sender, ICommandReply reply) { /* We encounter a bit of an issue here. Upon doing some research it is easily resolved * by couchdb. Lets examine. * * Issue: We update the resource to the server, connection is lost, we try to delete the * versions newer than the target version of the rollback. This fails. We now have * zombie versions that will block future recreation of those versions as the IDs will * be the same. * * Solution: "Updating existing documents requires setting the _rev member to the * revision being updated. To delete a document set the _deleted member to true." * http://wiki.apache.org/couchdb/HTTP_Bulk_Document_API. * * Example: * { * "docs": [ * {"_id": "0", "_rev": "1-62657917", "_deleted": true}, * {"_id": "1", "_rev": "1-2089673485", "integer": 2, "string": "2"}, * {"_id": "2", "_rev": "1-2063452834", "integer": 3, "string": "3"} * ] * } * * Implementation: So, we already have bulk document post support. We simply need to * load the bulkdocument with the resource and the _id and _rev for each version we want * to remove. We will also want to set the _deleted to true. */ // 1) Download Resource // 2) Check for VersionControl permission // 3) Modify Resource (VersionIds, CurrentVersionId) // 4) Make bulk document // 5) Upload bulk document Type t = sender.GetType(); if (t == typeof(Tasks.DownloadResource)) { Tasks.DownloadResource task = (Tasks.DownloadResource)sender; _resource = task.Resource; _remainder = task.Remainder; RunTaskProcess(new Tasks.CheckResourcePermissions(_db, _resource, _requestingPartyType, _session, Security.Authorization.ResourcePermissionType.VersionControl, _sendTimeout, _receiveTimeout, _sendBufferSize, _receiveBufferSize)); } else if (t == typeof(Tasks.CheckResourcePermissions)) { Data.VersionId oldCurrentVersionId; Tasks.CheckResourcePermissions task = (Tasks.CheckResourcePermissions)sender; if (!task.IsAuthorized) { TriggerOnAuthorizationDenied(task); return; } // If version number < rollback -> error // Versions: 0,1,2 / rollback: 2 -> 0th version if (_resource.CurrentVersionId.VersionNumber < _rollbackDepth) { TriggerOnError(null, "Rollback depth out of range.", null); return; } else if (_rollbackDepth <= 0) { TriggerOnError(null, "Rollback depth must be a positive value.", null); return; } oldCurrentVersionId = _resource.CurrentVersionId; _versionsToDelete = new Dictionary <Data.VersionId, Data.Version>(); long targetVersionNumber = _resource.CurrentVersionId.VersionNumber - _rollbackDepth; for (int i = 0; i < _resource.VersionIds.Count; i++) { if (_resource.VersionIds[i].VersionNumber > targetVersionNumber) { Data.VersionId vid = new Data.VersionId(_resource.ResourceId, i); _versionsToDelete.Add(vid, new Data.Version(vid)); } } // Removes the versions more recent than the rollback point _resource.VersionIds.RemoveRange(_resource.VersionIds.Count - _rollbackDepth, _rollbackDepth); _resource.UpdateCurrentVersionBasedOnVersionsList(); Dictionary <Data.VersionId, Data.Version> .Enumerator en = _versionsToDelete.GetEnumerator(); // Dispatch all our heads to get the revisions // *note* do not combine these loops - we want the full list before starting while (en.MoveNext()) { RunTaskProcess(new Tasks.HeadVersion(_db, en.Current.Key, _sendTimeout, _receiveTimeout, _sendBufferSize, _receiveBufferSize)); } } else if (t == typeof(Tasks.HeadVersion)) { Tasks.HeadVersion task = (Tasks.HeadVersion)sender; if (!_versionsToDelete.ContainsKey(task.VersionId)) { TriggerOnError(task, "The id '" + task.VersionId.ToString() + "' could not be found.", new KeyNotFoundException()); return; } lock (_versionsToDelete) { _receivedCount++; _versionsToDelete[task.VersionId].UpdateRevision(task.Revision); if (_versionsToDelete.Count == _receivedCount) { // Inside here we have a collection "docs" that contains the new resource // which has the new "current" version and has all the more recent // versions removed. We also have inside "docs" deletion markers for all // the more recent versions. List <Exception> errors; List <Model.Document> docs = new List <Model.Document>(); Transitions.Resource txResource = new Transitions.Resource(); Model.Document doc = txResource.Transition(_resource, out errors); if (errors != null) { TriggerOnError(null, errors[0].Message, errors[0]); return; } doc.CombineWith(_remainder); docs.Add(doc); Dictionary <Data.VersionId, Data.Version> .Enumerator en = _versionsToDelete.GetEnumerator(); // Dispatch all our heads to get the revisions // *note* do not combine these loops - we want the full list before starting while (en.MoveNext()) { Transitions.Version txVersion = new Transitions.Version(); Model.Document doc2 = txVersion.Transition(en.Current.Value, out errors); if (errors != null) { TriggerOnError(null, errors[0].Message, errors[0]); return; } doc2.Add("_deleted", true); docs.Add(doc2); } RunTaskProcess(new Tasks.MakeBulkDocument(docs, _sendTimeout, _receiveTimeout, _sendBufferSize, _receiveBufferSize)); } } } else if (t == typeof(Tasks.MakeBulkDocument)) { Tasks.MakeBulkDocument task = (Tasks.MakeBulkDocument)sender; RunTaskProcess(new Tasks.UploadBulkDocuments(_db, task.BulkDocument, _sendTimeout, _receiveTimeout, _sendBufferSize, _receiveBufferSize)); } else if (t == typeof(Tasks.UploadBulkDocuments)) { Tasks.UploadBulkDocuments task = (Tasks.UploadBulkDocuments)sender; TriggerOnComplete(reply, task.Results); } else { TriggerOnError(sender, reply.ToString(), null); } }
public static Uri Build(IDatabase db, Data.VersionId versionId) { return(new Uri(db.Uri.ToString() + versionId.ToString())); }
public virtual void CheckoutVersion(EngineRequest request, Data.VersionId version) { throw new NotImplementedException(); }
public override void TaskComplete(Tasks.Base sender, ICommandReply reply) { /* We encounter a bit of an issue here. Upon doing some research it is easily resolved * by couchdb. Lets examine. * * Issue: We update the resource to the server, connection is lost, we try to delete the * versions newer than the target version of the rollback. This fails. We now have * zombie versions that will block future recreation of those versions as the IDs will * be the same. * * Solution: "Updating existing documents requires setting the _rev member to the * revision being updated. To delete a document set the _deleted member to true." * http://wiki.apache.org/couchdb/HTTP_Bulk_Document_API. * * Example: * { * "docs": [ * {"_id": "0", "_rev": "1-62657917", "_deleted": true}, * {"_id": "1", "_rev": "1-2089673485", "integer": 2, "string": "2"}, * {"_id": "2", "_rev": "1-2063452834", "integer": 3, "string": "3"} * ] * } * * Implementation: So, we already have bulk document post support. We simply need to * load the bulkdocument with the resource and the _id and _rev for each version we want * to remove. We will also want to set the _deleted to true. */ // 1) Download Resource // 2) Check for VersionControl permission // 3) Modify Resource (VersionIds, CurrentVersionId) // 4) Make bulk document // 5) Upload bulk document Type t = sender.GetType(); if (t == typeof(Tasks.DownloadResource)) { Tasks.DownloadResource task = (Tasks.DownloadResource)sender; _resource = task.Resource; _remainder = task.Remainder; RunTaskProcess(new Tasks.CheckResourcePermissions(_db, _resource, _requestingPartyType, _session, Security.Authorization.ResourcePermissionType.VersionControl, _sendTimeout, _receiveTimeout, _sendBufferSize, _receiveBufferSize)); } else if (t == typeof(Tasks.CheckResourcePermissions)) { Data.VersionId oldCurrentVersionId; Tasks.CheckResourcePermissions task = (Tasks.CheckResourcePermissions)sender; if (!task.IsAuthorized) { TriggerOnAuthorizationDenied(task); return; } // If version number < rollback -> error // Versions: 0,1,2 / rollback: 2 -> 0th version if (_resource.CurrentVersionId.VersionNumber < _rollbackDepth) { TriggerOnError(null, "Rollback depth out of range.", null); return; } else if (_rollbackDepth <= 0) { TriggerOnError(null, "Rollback depth must be a positive value.", null); return; } oldCurrentVersionId = _resource.CurrentVersionId; _versionsToDelete = new Dictionary<Data.VersionId, Data.Version>(); long targetVersionNumber = _resource.CurrentVersionId.VersionNumber - _rollbackDepth; for(int i=0; i<_resource.VersionIds.Count; i++) { if (_resource.VersionIds[i].VersionNumber > targetVersionNumber) { Data.VersionId vid = new Data.VersionId(_resource.ResourceId, i); _versionsToDelete.Add(vid, new Data.Version(vid)); } } // Removes the versions more recent than the rollback point _resource.VersionIds.RemoveRange(_resource.VersionIds.Count - _rollbackDepth, _rollbackDepth); _resource.UpdateCurrentVersionBasedOnVersionsList(); Dictionary<Data.VersionId, Data.Version>.Enumerator en = _versionsToDelete.GetEnumerator(); // Dispatch all our heads to get the revisions // *note* do not combine these loops - we want the full list before starting while (en.MoveNext()) { RunTaskProcess(new Tasks.HeadVersion(_db, en.Current.Key, _sendTimeout, _receiveTimeout, _sendBufferSize, _receiveBufferSize)); } } else if (t == typeof(Tasks.HeadVersion)) { Tasks.HeadVersion task = (Tasks.HeadVersion)sender; if (!_versionsToDelete.ContainsKey(task.VersionId)) { TriggerOnError(task, "The id '" + task.VersionId.ToString() + "' could not be found.", new KeyNotFoundException()); return; } lock (_versionsToDelete) { _receivedCount++; _versionsToDelete[task.VersionId].UpdateRevision(task.Revision); if (_versionsToDelete.Count == _receivedCount) { // Inside here we have a collection "docs" that contains the new resource // which has the new "current" version and has all the more recent // versions removed. We also have inside "docs" deletion markers for all // the more recent versions. List<Exception> errors; List<Model.Document> docs = new List<Model.Document>(); Transitions.Resource txResource = new Transitions.Resource(); Model.Document doc = txResource.Transition(_resource, out errors); if (errors != null) { TriggerOnError(null, errors[0].Message, errors[0]); return; } doc.CombineWith(_remainder); docs.Add(doc); Dictionary<Data.VersionId, Data.Version>.Enumerator en = _versionsToDelete.GetEnumerator(); // Dispatch all our heads to get the revisions // *note* do not combine these loops - we want the full list before starting while (en.MoveNext()) { Transitions.Version txVersion = new Transitions.Version(); Model.Document doc2 = txVersion.Transition(en.Current.Value, out errors); if (errors != null) { TriggerOnError(null, errors[0].Message, errors[0]); return; } doc2.Add("_deleted", true); docs.Add(doc2); } RunTaskProcess(new Tasks.MakeBulkDocument(docs, _sendTimeout, _receiveTimeout, _sendBufferSize, _receiveBufferSize)); } } } else if (t == typeof(Tasks.MakeBulkDocument)) { Tasks.MakeBulkDocument task = (Tasks.MakeBulkDocument)sender; RunTaskProcess(new Tasks.UploadBulkDocuments(_db, task.BulkDocument, _sendTimeout, _receiveTimeout, _sendBufferSize, _receiveBufferSize)); } else if (t == typeof(Tasks.UploadBulkDocuments)) { Tasks.UploadBulkDocuments task = (Tasks.UploadBulkDocuments)sender; TriggerOnComplete(reply, task.Results); } else { TriggerOnError(sender, reply.ToString(), null); } }