public DocumentDescriptorId FindDuplicateDocumentId(
            DocumentDescriptorId sourceDocumentId, 
            FileHash sourceHash,
            BlobId sourceBlobId 
            )
        {
            if (!_config.IsDeduplicationActive)
                return null;

            var original = _blobStore.GetDescriptor(sourceBlobId);

            var matches = _hashReader.FindDocumentByHash(sourceHash);
            Logger.DebugFormat("Deduplicating document {0}", sourceDocumentId);
            foreach (var match in matches)
            {
                if (match.DocumentDescriptorId == sourceDocumentId)
                    continue;

                Logger.DebugFormat("Checking document {0}", match.DocumentDescriptorId);

                var candidate = this._blobStore.GetDescriptor(match.BlobId);
                // only within same content type!
                if (candidate.ContentType != original.ContentType)
                {
                    Logger.DebugFormat("document {0} has different ContentType ({1}), skipping",
                        match.DocumentDescriptorId, candidate.ContentType
                        );
                    continue;
                }

                // and same length
                if (candidate.Length != original.Length)
                {
                    Logger.DebugFormat("document {0} has different length ({1}), skipping",
                        match.DocumentDescriptorId, candidate.Length
                        );
                    continue;
                }
             
                // binary check
                using (var candidateStream = candidate.OpenRead())
                using (var originalStream = original.OpenRead())
                {
                    if (StreamHelper.StreamsContentsAreEqual(candidateStream, originalStream))
                    {
                        Logger.DebugFormat("{0} has same content of {1}: match found!",
                            match.DocumentDescriptorId, sourceDocumentId
                            );
                        return match.DocumentDescriptorId;
                    }
                    else
                    {
                        Logger.DebugFormat("{0} has different content of {1}, skipping",
                            match.DocumentDescriptorId, sourceDocumentId
                            );                        
                    }
                }
            }
            return null;
        }
 public DocumentDeleted(DocumentHandle handle, DocumentDescriptorId documentDescriptorId)
 {
     if (handle == null) throw new ArgumentNullException("handle");
     if (documentDescriptorId == null) throw new ArgumentNullException("documentDescriptorId");
     DocumentDescriptorId = documentDescriptorId;
     Handle = handle;
 }
 public DocumentReadModel(DocumentHandle handle, DocumentDescriptorId documentid, FileNameWithExtension fileName, DocumentCustomData customData)
 {
     Handle = handle;
     DocumentDescriptorId = documentid;
     FileName = fileName;
     CustomData = customData;
 }
 public DocumentLinked(DocumentHandle handle, DocumentDescriptorId documentId, DocumentDescriptorId previousDocumentId, FileNameWithExtension fileName)
 {
     FileName = fileName;
     PreviousDocumentId = previousDocumentId;
     Handle = handle;
     DocumentId = documentId;
 }
 public LinkDocumentToDocumentDescriptor(
     DocumentDescriptorId documentDescriptorId,
     DocumentHandleInfo handleInfo)
 {
     DocumentDescriptorId = documentDescriptorId;
     HandleInfo = handleInfo;
 }
 public CreateDocumentAsCopy(
     DocumentHandle handle, 
     DocumentDescriptorId descriptorId,
     DocumentHandleInfo handleInfo)
 {
     Handle = handle;
     DocumentDescriptorId = descriptorId;
     HandleInfo = handleInfo;
 }
 /// <summary>
 /// This DocumentDescriptor has the same content of another <see cref="DocumentDescriptor"/>
 /// this operation mark the current document as owner of the handle of duplicated document
 /// descriptor
 /// </summary>
 /// <param name="otherDocumentDescriptorId"></param>
 /// <param name="handle"></param>
 /// <param name="fileName"></param>
 public void Deduplicate(
     DocumentDescriptorId otherDocumentDescriptorId,
     DocumentHandleInfo handleInfo)
 {
     ThrowIfDeleted(String.Format("Deduplicate with {0}", otherDocumentDescriptorId));
     RaiseEvent(new DocumentDescriptorHasBeenDeduplicated(
                    otherDocumentDescriptorId,
                    handleInfo));
     Attach(handleInfo);
 }
 public DocumentCopied(
     DocumentHandle handle, 
     DocumentDescriptorId documentDescriptorId,
     DocumentHandleInfo handleInfo)
 {
     if (handle == null) throw new ArgumentNullException("handle");
     if (documentDescriptorId == null) throw new ArgumentNullException("documentDescriptorId");
     if (handleInfo == null) throw new ArgumentNullException("handleInfo");
     DocumentDescriptorId = documentDescriptorId;
     NewHandle = handle;
     HandleInfo = handleInfo;
 }
        public void InitializeAsAttach(
            BlobId blobId,
            DocumentHandleInfo handleInfo,
            FileHash hash,
            String fileName,
            DocumentDescriptorId fatherDocumentDescriptorId)
        {
            ThrowIfDeleted(String.Format("Initialize as attach with blob {0} and fileName {1}", blobId, fileName));

            if (HasBeenCreated)
            {
                throw new DomainException(Id, "Already initialized");
            }

            RaiseEvent(new DocumentDescriptorInitialized(blobId, handleInfo, hash, fatherDocumentDescriptorId));

            var knownFormat = DocumentFormatTranslator.GetFormatFromFileName(fileName);

            if (knownFormat != null)
            {
                RaiseEvent(new FormatAddedToDocumentDescriptor(knownFormat, blobId, null));
            }
        }
 public void LinkDocument(DocumentHandle handle, DocumentDescriptorId id, long projectedAt)
 {
     InnerCreateLinkToDocument(handle, id, null, projectedAt);
 }
        public void Projected(int expectedDocId, int projectedAt, bool isPending)
        {
            var expectedDocumentId = new DocumentDescriptorId(expectedDocId);
            _writer.Promise(_documentHandle, 10);
            _writer.LinkDocument(_documentHandle, _document2, projectedAt);

            var h = _writer.FindOneById(_documentHandle);
            Assert.NotNull(h);
            Assert.AreEqual(10, h.CreatetAt);
            Assert.IsNull(h.FileName);

            if (h.ProjectedAt >= h.CreatetAt)
            {
                Assert.AreEqual(expectedDocumentId, h.DocumentDescriptorId);
                Assert.AreEqual(projectedAt, h.ProjectedAt);
            }

            Assert.AreEqual(isPending, h.IsPending());
        }
 public DocumentReadModel(DocumentHandle handle, DocumentDescriptorId documentid, FileNameWithExtension fileName)
     : this(handle, documentid, fileName, null)
 {
 }
 public void CreateIfMissing(DocumentHandle handle, DocumentDescriptorId documentDescriptorId, long createdAt)
 {
     Logger.DebugFormat("CreateIfMissing on handle {0} [{1}]", handle, createdAt);
     Int32 iteration = 0;
     do
     {
         try
         {
             var result = _collection.FindOneAndUpdate(
                 Builders<DocumentReadModel>.Filter.Eq(x => x.Handle, handle),
                 Builders<DocumentReadModel>.Update
                     .SetOnInsert(x => x.CustomData, null)
                     .SetOnInsert(x => x.ProjectedAt, 0)
                     .SetOnInsert(x => x.DocumentDescriptorId, documentDescriptorId)
                     .SetOnInsert(x => x.CreatetAt, createdAt)
                     .SetOnInsert(x => x.FileName, null),
                 new FindOneAndUpdateOptions<DocumentReadModel, DocumentReadModel>()
                 {
                     ReturnDocument = ReturnDocument.After,
                     IsUpsert = true
                 });
         }
         catch (MongoCommandException ex)
         {
             if (ex.Message.Contains("E11000") == false)
                 throw;
         }
     } while (iteration++ < 3);
    
 }
        private Boolean InnerCreateLinkToDocument(DocumentHandle handle, DocumentDescriptorId id, Boolean? deDuplication, long projectedAt)
        {
            Logger.DebugFormat("LinkDocument on handle {0} [{1}]", handle, projectedAt);

            var result = _collection.FindOneAndUpdate(
               Builders<DocumentReadModel>.Filter.And(
                  Builders<DocumentReadModel>.Filter.Eq(x => x.Handle, handle),
                  Builders<DocumentReadModel>.Filter.Ne(x => x.DocumentDescriptorId, id),
                  Builders<DocumentReadModel>.Filter.Lte(x => x.CreatetAt, projectedAt)
              ),
               Builders<DocumentReadModel>.Update
                   .Set(x => x.DocumentDescriptorId, id)
                   .Set(x => x.ProjectedAt, projectedAt),
            new FindOneAndUpdateOptions<DocumentReadModel, DocumentReadModel>()
               {
                   ReturnDocument = ReturnDocument.After
               }
              );

            if (Logger.IsDebugEnabled)
            {
                Logger.DebugFormat("LinkDocument on handle {0} [{1}] : {2}", handle, projectedAt, result != null);
            }
            return result != null;
        }
 protected void SetupDocumentHandle(DocumentHandleInfo handleInfo, DocumentDescriptorId documentId)
 {
     _handleWriter
         .FindOneById(handleInfo.Handle)
         .Returns(info => new DocumentReadModel(handleInfo.Handle, documentId, handleInfo.FileName));
 }
 private void CreateDocument(
     DocumentDescriptorId documentDescriptorId,
     BlobId blobId,
     DocumentHandle handle,
     DocumentHandle fatherHandle,
     DocumentDescriptorId fatherDocumentDescriptorId,
     FileNameWithExtension fileName,
     DocumentCustomData customData
 )
 {
     var descriptor = _blobStore.GetDescriptor(blobId);
     ICommand createDocument;
     var handleInfo = new DocumentHandleInfo(handle, fileName, customData);
     if (fatherHandle == null)
     {
         if (Logger.IsDebugEnabled) Logger.DebugFormat("Initialize DocumentDescriptor {0} ", documentDescriptorId);
         createDocument = new InitializeDocumentDescriptor(documentDescriptorId, blobId, handleInfo, descriptor.Hash, fileName);
     }
     else
     {
         if (Logger.IsDebugEnabled) Logger.DebugFormat("Initialize DocumentDescriptor as attach {0} ", documentDescriptorId);
         createDocument = new InitializeDocumentDescriptorAsAttach(
             documentDescriptorId,
             blobId,
             handleInfo,
             fatherHandle,
             fatherDocumentDescriptorId,
             descriptor.Hash, fileName);
     }
     CommandBus.Send(createDocument, "api");
 }
 public Match(DocumentDescriptorId documentDescriptorId, BlobId blobId)
 {
     DocumentDescriptorId = documentDescriptorId;
     BlobId = blobId;
 }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="tenantId"></param>
        /// <param name="handle"></param>
        /// <param name="fatherHandle">Different from null only when you want to upload
        /// a document that is an attachment of another document</param>
        /// <param name="fatherHandleDescriptorId">Descriptor id that contains reference
        /// to <paramref name="fatherHandle"/></param>
        /// <returns></returns>
        private async Task<HttpResponseMessage> InnerUploadDocument(
            TenantId tenantId,
            DocumentHandle handle,
            DocumentHandle fatherHandle,
            DocumentDescriptorId fatherHandleDescriptorId)
        {
            var documentId = _identityGenerator.New<DocumentDescriptorId>();

            Logger.DebugFormat("Incoming file {0}, assigned {1}", handle, documentId);
            var errorMessage = await UploadFromHttpContent(Request.Content);
            Logger.DebugFormat("File {0} processed with message {1}", _blobId, errorMessage ?? "OK");

            if (errorMessage != null)
            {
                return Request.CreateErrorResponse(
                    HttpStatusCode.BadRequest,
                    errorMessage
                );
            }

            CreateDocument(documentId, _blobId, handle, fatherHandle, fatherHandleDescriptorId, _fileName, _customData);

            Logger.DebugFormat("File {0} uploaded as {1}", _blobId, documentId);

            var storedFile = _blobStore.GetDescriptor(_blobId);

            return Request.CreateResponse(
                HttpStatusCode.OK,
                new UploadedDocumentResponse
                {
                    Handle = handle,
                    Hash = storedFile.Hash,
                    HashType = "md5",
                    Uri = Url.Content("/" + tenantId + "/documents/" + handle)
                }
            );
        }
 public void DocumentDeDuplicated(
     DocumentHandle handle,
     DocumentHandle primaryHandle,
     DocumentDescriptorId id,
     long projectedAt)
 {
     var linkChanged = InnerCreateLinkToDocument(handle, id, true, projectedAt);
 }
 public void Link(DocumentDescriptorId documentId)
 {
     this.LinkedDocument = documentId;
 }
 /// <summary>
 /// when a document descriptor is deleted there is no need to continue
 /// scheduling jobs, so all jobs should be deleted.
 /// </summary>
 /// <param name="documentDescriptorId"></param>
 internal void DeletedJobForDescriptor(DocumentDescriptorId documentDescriptorId)
 {
     _collection.DeleteMany(
         Builders<QueuedJob>.Filter.Eq(j => j.DocumentDescriptorId, documentDescriptorId));
 }