public override VetoResult AllowPut(string key, RavenJObject document, RavenJObject metadata, TransactionInformation transactionInformation)
        {
            // always reset these
            _clearCurrent.Value = false;
            _originalDocument.Value = null;
            _now.Value = SystemTime.UtcNow;

            if (key == null)
                return VetoResult.Allowed;

            // Don't do anything if temporal versioning is inactive for this document type
            if (!Database.IsTemporalVersioningEnabled(key, metadata))
                return VetoResult.Allowed;

            // Don't allow modifications to revision documents
            if (key.Contains(TemporalConstants.TemporalKeySeparator))
                return VetoResult.Deny("Modifying an existing temporal revision directly is not allowed.");

            // If no effective date was passed in, use now.
            var temporal = metadata.GetTemporalMetadata();
            if (!temporal.Effective.HasValue)
                temporal.Effective = _now.Value;

            return VetoResult.Allowed;
        }
        public override VetoResult AllowPut(string key, RavenJObject document, RavenJObject metadata, TransactionInformation transactionInformation)
        {
            // always reset these
            _clearCurrent.Value     = false;
            _originalDocument.Value = null;
            _now.Value = SystemTime.UtcNow;

            if (key == null)
            {
                return(VetoResult.Allowed);
            }

            // Don't do anything if temporal versioning is inactive for this document type
            if (!Database.IsTemporalVersioningEnabled(key, metadata))
            {
                return(VetoResult.Allowed);
            }

            // Don't allow modifications to revision documents
            if (key.Contains(TemporalConstants.TemporalKeySeparator))
            {
                return(VetoResult.Deny("Modifying an existing temporal revision directly is not allowed."));
            }

            // If no effective date was passed in, use now.
            var temporal = metadata.GetTemporalMetadata();

            if (!temporal.Effective.HasValue)
            {
                temporal.Effective = _now.Value;
            }

            return(VetoResult.Allowed);
        }
        public override void OnRead(string key, RavenJObject document, RavenJObject metadata, ReadOperation operation,
                                    TransactionInformation transactionInformation)
        {
            // This trigger is only for simple query operations
            if (key == null || operation != ReadOperation.Query)
            {
                return;
            }

            // Don't do anything when temporal versioning is not enabled
            if (!Database.IsTemporalVersioningEnabled(key, metadata))
            {
                return;
            }

            // Only operate on temporal revisions
            var temporal = metadata.GetTemporalMetadata();

            if (temporal.Status != TemporalStatus.Revision)
            {
                return;
            }

            // Send back the revision number
            temporal.RevisionNumber = int.Parse(key.Split('/').Last());

            // When we filtered by effective date, return the document id instead of the revision id
            if (temporal.Effective.HasValue)
            {
                metadata["@id"] = key.Substring(0, key.IndexOf(TemporalConstants.TemporalKeySeparator, StringComparison.Ordinal));
            }
        }
        public override ReadVetoResult AllowRead(string key, RavenJObject metadata, ReadOperation operation, TransactionInformation transactionInformation)
        {
            // This trigger is only for simple query operations
            if (key == null || operation != ReadOperation.Query)
                return ReadVetoResult.Allowed;

            // Don't do anything if temporal versioning is inactive for this document type
            if (!Database.IsTemporalVersioningEnabled(key, metadata))
                return ReadVetoResult.Allowed;

            // If an effective date was passed in, then use it.
            DateTimeOffset effective;
            var headerValue = CurrentOperationContext.Headers.Value[TemporalMetadata.RavenTemporalEffective];
            if (headerValue == null || !DateTimeOffset.TryParse(headerValue, null, DateTimeStyles.RoundtripKind, out effective))
            {
                // If no effective data passed, return as stored.
                return ReadVetoResult.Allowed;
            }

            // Return the requested effective date in the metadata.
            var temporal = metadata.GetTemporalMetadata();
            temporal.Effective = effective;

            // Return the result if it's the active revision, or skip it otherwise.
            return temporal.Status == TemporalStatus.Revision &&
                   temporal.EffectiveStart <= effective && effective < temporal.EffectiveUntil &&
                   !temporal.Deleted
                       ? ReadVetoResult.Allowed
                       : ReadVetoResult.Ignore;
        }
        public override ReadVetoResult AllowRead(string key, RavenJObject metadata, ReadOperation operation, TransactionInformation transactionInformation)
        {
            // always reset these
            _temporalVersioningEnabled.Value = false;
            _effectiveVersionKey.Value = null;
            _now.Value = SystemTime.UtcNow;

            if (key == null)
                return ReadVetoResult.Allowed;

            // This trigger is only for load operations
            if (operation != ReadOperation.Load)
                return ReadVetoResult.Allowed;

            // Don't do anything if temporal versioning is inactive for this document type
            _temporalVersioningEnabled.Value = Database.IsTemporalVersioningEnabled(key, metadata);
            if (!_temporalVersioningEnabled.Value)
                return ReadVetoResult.Allowed;

            // Only operate on current temporal documents
            var temporal = metadata.GetTemporalMetadata();
            if (temporal.Status != TemporalStatus.Current)
                return ReadVetoResult.Allowed;

            // If an effective date was passed in, then use it.
            DateTimeOffset effective;
            var headerValue = CurrentOperationContext.Headers.Value[TemporalMetadata.RavenTemporalEffective];
            if (headerValue == null || !DateTimeOffset.TryParse(headerValue, null, DateTimeStyles.RoundtripKind, out effective))
            {
                // If no effective data passed, return current data, as stored, effective now.
                temporal.Effective = _now.Value;
                return ReadVetoResult.Allowed;
            }

            // Return the requested effective date in the metadata.
            temporal.Effective = effective;

            // If the current document is already in range, just return it
            if (temporal.EffectiveStart <= effective && effective < temporal.EffectiveUntil)
                return ReadVetoResult.Allowed;

            // Load the history doc
            Etag historyEtag;
            var history = Database.GetTemporalHistoryFor(key, transactionInformation, out historyEtag);

            // Find the revision that is effective at the date requested
            var effectiveRevisionInfo = history.Revisions.FirstOrDefault(x => x.Status == TemporalStatus.Revision &&
                                                                              x.EffectiveStart <= effective &&
                                                                              x.EffectiveUntil > effective);
            // Return nothing if there is no revision at the effective date
            if (effectiveRevisionInfo == null)
                return ReadVetoResult.Ignore;

            // Hold on to the key so we can use it later in OnRead
            _effectiveVersionKey.Value = effectiveRevisionInfo.Key;
            return ReadVetoResult.Allowed;
        }
        public void BeforeDelete(string key, object entityInstance, RavenJObject metadata)
        {
            var temporal = metadata.GetTemporalMetadata();
            if (temporal.Status == TemporalStatus.NonTemporal)
                return;

            var session = (DocumentSession) ThreadLocalSession.Value;
            var headers = session.DatabaseCommands.OperationsHeaders;
            var header = string.Format("{0}-{1}", TemporalMetadata.RavenTemporalEffective, key.Replace('/', '-'));

            var effective = temporal.Effective ?? SystemTime.UtcNow;
            headers[header] = effective.ToString("o");
        }
        public void BeforeDelete(string key, object entityInstance, RavenJObject metadata)
        {
            var temporal = metadata.GetTemporalMetadata();
            if (temporal.Status == TemporalStatus.NonTemporal)
                return;

            if (!temporal.Effective.HasValue)
                throw new InvalidOperationException(
                    "No effective date was set for the temporal delete operation.  Be sure to specify an effective date when loading the entity to be deleted.");

            var session = (DocumentSession) ThreadLocalSession.Value;
            var headers = session.DatabaseCommands.OperationsHeaders;
            var header = string.Format("{0}-{1}", TemporalConstants.EffectiveDateHeader, key.Replace('/', '-'));
            headers[header] = temporal.Effective.Value.ToString("o");
        }
        public void BeforeDelete(string key, object entityInstance, RavenJObject metadata)
        {
            var temporal = metadata.GetTemporalMetadata();

            if (temporal.Status == TemporalStatus.NonTemporal)
            {
                return;
            }

            var session = (DocumentSession)ThreadLocalSession.Value;
            var headers = session.DatabaseCommands.OperationsHeaders;
            var header  = string.Format("{0}-{1}", TemporalMetadata.RavenTemporalEffective, key.Replace('/', '-'));

            var effective = temporal.Effective ?? SystemTime.UtcNow;

            headers[header] = effective.ToString("o");
        }
예제 #9
0
        public void BeforeDelete(string key, object entityInstance, RavenJObject metadata)
        {
            var temporal = metadata.GetTemporalMetadata();

            if (temporal.Status == TemporalStatus.NonTemporal)
            {
                return;
            }

            if (!temporal.Effective.HasValue)
            {
                throw new InvalidOperationException(
                          "No effective date was set for the temporal delete operation.  Be sure to specify an effective date when loading the entity to be deleted.");
            }

            var session = (DocumentSession)ThreadLocalSession.Value;
            var headers = session.DatabaseCommands.OperationsHeaders;
            var header  = string.Format("{0}-{1}", TemporalConstants.EffectiveDateHeader, key.Replace('/', '-'));

            headers[header] = temporal.Effective.Value.ToString("o");
        }
        public override void OnRead(string key, RavenJObject document, RavenJObject metadata, ReadOperation operation,
            TransactionInformation transactionInformation)
        {
            // This trigger is only for simple query operations
            if (key == null || operation != ReadOperation.Query)
                return;

            // Don't do anything when temporal versioning is not enabled
            if (!Database.IsTemporalVersioningEnabled(key, metadata))
                return;

            // Only operate on temporal revisions
            var temporal = metadata.GetTemporalMetadata();
            if (temporal.Status != TemporalStatus.Revision)
                return;

            // Send back the revision number
            temporal.RevisionNumber = int.Parse(key.Split('/').Last());

            // When we filtered by effective date, return the document id instead of the revision id
            if (temporal.Effective.HasValue)
                metadata["@id"] = key.Substring(0, key.IndexOf(TemporalConstants.TemporalKeySeparator, StringComparison.Ordinal));
        }
        public override ReadVetoResult AllowRead(string key, RavenJObject metadata, ReadOperation operation, TransactionInformation transactionInformation)
        {
            // This trigger is only for simple query operations
            if (key == null || operation != ReadOperation.Query)
            {
                return(ReadVetoResult.Allowed);
            }

            // Don't do anything if temporal versioning is inactive for this document type
            if (!Database.IsTemporalVersioningEnabled(key, metadata))
            {
                return(ReadVetoResult.Allowed);
            }

            // If an effective date was passed in, then use it.
            DateTimeOffset effective;
            var            headerValue = CurrentOperationContext.Headers.Value[TemporalMetadata.RavenTemporalEffective];

            if (headerValue == null || !DateTimeOffset.TryParse(headerValue, null, DateTimeStyles.RoundtripKind, out effective))
            {
                // If no effective data passed, return as stored.
                return(ReadVetoResult.Allowed);
            }

            // Return the requested effective date in the metadata.
            var temporal = metadata.GetTemporalMetadata();

            temporal.Effective = effective;

            // Return the result if it's the active revision, or skip it otherwise.
            return(temporal.Status == TemporalStatus.Revision &&
                   temporal.EffectiveStart <= effective && effective < temporal.EffectiveUntil &&
                   !temporal.Deleted
                       ? ReadVetoResult.Allowed
                       : ReadVetoResult.Ignore);
        }
        public override void OnRead(string key, RavenJObject document, RavenJObject metadata, ReadOperation operation,
            TransactionInformation transactionInformation)
        {
            if (key == null)
                return;

            // If we're loading a revision directly, make sure we have set the rev number in the metadata
            var temporal = metadata.GetTemporalMetadata();
            if (key.Contains(TemporalConstants.TemporalKeySeparator))
                temporal.RevisionNumber = int.Parse(key.Split('/').Last());

            // Handle migration from nontemporal document
            if (temporal.Status == TemporalStatus.NonTemporal && _temporalVersioningEnabled.Value)
            {
                // Rewrite the document temporally.  We specifically do NOT disable triggers on this put.
                temporal.Effective = DateTimeOffset.MinValue;
                Database.Put(key, null, new RavenJObject(document), new RavenJObject(metadata), transactionInformation);

                // Fake out the current document for the return of this load.
                temporal.Status = TemporalStatus.Current;
                temporal.RevisionNumber = 1;
                temporal.Effective = _now.Value;
                temporal.EffectiveStart = DateTimeOffset.MinValue;
                temporal.EffectiveUntil = DateTimeOffset.MaxValue;
            }

            // If we didn't get a new effective revision key above, just return
            var effectiveRevisionKey = _effectiveVersionKey.Value;
            if (effectiveRevisionKey == null)
                return;

            _log.Debug("Temporally loading {0} instead of {1}", effectiveRevisionKey, key);

            using (Database.DisableAllTriggersForCurrentThread())
            {
                // Load the effective document
                var effectiveRevision = Database.Get(effectiveRevisionKey, transactionInformation);

                // Replace the resulting document
                foreach (var prop in document.Keys)
                    document.Remove(prop);
                var evDoc = effectiveRevision.DataAsJson;
                foreach (var prop in evDoc.Keys)
                    document.Add(prop, evDoc[prop]);

                // Replace the resulting metadata
                foreach (var prop in metadata.Keys)
                {
                    if (prop != TemporalMetadata.RavenTemporalEffective)
                        metadata.Remove(prop);
                }
                var evMetadata = effectiveRevision.Metadata;
                foreach (var prop in evMetadata.Keys)
                    metadata.Add(prop, evMetadata[prop]);

                // Send back the revision number also
                temporal.RevisionNumber = int.Parse(effectiveRevisionKey.Split('/').Last());
            }
        }
        public static int PutRevision(this DocumentDatabase database, string key, RavenJObject document, RavenJObject metadata,
                                      TransactionInformation transactionInformation, DateTime now, bool deleted = false)
        {
            Log.Debug("Putting new temporal revision for {0}", key);

            // The revision is a copy of the document.
            var revisionDocument = new RavenJObject(document);
            var revisionMetadata = new RavenJObject(metadata);

            // Set metadata on the revision
            var temporal  = revisionMetadata.GetTemporalMetadata();
            var effective = temporal.EffectiveStart.GetValueOrDefault();

            temporal.Status  = TemporalStatus.Revision;
            temporal.Deleted = deleted;
            temporal.Pending = effective > now;

            // Store the revision
            var newRevisionDoc = database.Put(key + TemporalConstants.TemporalKeySeparator, null,
                                              revisionDocument, revisionMetadata,
                                              transactionInformation);

            // Get the revision number that was generated
            var revisionNumber = int.Parse(newRevisionDoc.Key.Split('/').Last());

            // Get the history doc and add this revision
            Etag historyEtag;
            var  history = database.GetTemporalHistoryFor(key, transactionInformation, out historyEtag);

            history.AddRevision(newRevisionDoc.Key, temporal);

            if (revisionNumber > 1)
            {
                // Artifact any revisions that already exist on or after the new effective date
                var futureRevisions = history.Revisions.Where(x => x.Key != newRevisionDoc.Key &&
                                                              x.Status == TemporalStatus.Revision &&
                                                              x.EffectiveStart >= effective);
                foreach (var revisionInfo in futureRevisions)
                {
                    // in the history
                    revisionInfo.Status = TemporalStatus.Artifact;

                    // on the revision doc
                    database.SetDocumentMetadata(revisionInfo.Key, transactionInformation,
                                                 TemporalMetadata.RavenDocumentTemporalStatus,
                                                 TemporalStatus.Artifact.ToString());
                }

                // Update the until date of the last revison prior to this one
                var lastRevision = history.Revisions.LastOrDefault(x => x.Key != newRevisionDoc.Key &&
                                                                   x.Status == TemporalStatus.Revision &&
                                                                   x.EffectiveStart < effective);
                if (lastRevision != null)
                {
                    // in the history
                    lastRevision.EffectiveUntil = effective;
                    lastRevision.AssertedUntil  = now;

                    // on the revision doc
                    var md = new Dictionary <string, RavenJToken> {
                        { TemporalMetadata.RavenDocumentTemporalEffectiveUntil, effective },
                        { TemporalMetadata.RavenDocumentTemporalAssertedUntil, now }
                    };
                    database.SetDocumentMetadata(lastRevision.Key, transactionInformation, md);
                }
            }

            // Update the history doc
            database.SaveTemporalHistoryFor(key, history, transactionInformation, historyEtag);

            // Reset the activation timer with each put.
            // This is so future revisions can become current without having to constantly poll.
            database.StartupTasks.OfType <TemporalActivator>().Single().ResetTimer(effective.UtcDateTime, now);

            // Return the revision number
            return(revisionNumber);
        }
        public static int PutRevision(this DocumentDatabase database, string key, RavenJObject document, RavenJObject metadata,
            TransactionInformation transactionInformation, DateTime now, bool deleted = false)
        {
            Log.Debug("Putting new temporal revision for {0}", key);

            // The revision is a copy of the document.
            var revisionDocument = new RavenJObject(document);
            var revisionMetadata = new RavenJObject(metadata);

            // Set metadata on the revision
            var temporal = revisionMetadata.GetTemporalMetadata();
            var effective = temporal.EffectiveStart.GetValueOrDefault();
            temporal.Status = TemporalStatus.Revision;
            temporal.Deleted = deleted;
            temporal.Pending = effective > now;

            // Store the revision
            var newRevisionDoc = database.Put(key + TemporalConstants.TemporalKeySeparator, null,
                                              revisionDocument, revisionMetadata,
                                              transactionInformation);

            // Get the revision number that was generated
            var revisionNumber = int.Parse(newRevisionDoc.Key.Split('/').Last());

            // Get the history doc and add this revision
            Etag historyEtag;
            var history = database.GetTemporalHistoryFor(key, transactionInformation, out historyEtag);
            history.AddRevision(newRevisionDoc.Key, temporal);

            if (revisionNumber > 1)
            {
                // Artifact any revisions that already exist on or after the new effective date
                var futureRevisions = history.Revisions.Where(x => x.Key != newRevisionDoc.Key &&
                                                                   x.Status == TemporalStatus.Revision &&
                                                                   x.EffectiveStart >= effective);
                foreach (var revisionInfo in futureRevisions)
                {
                    // in the history
                    revisionInfo.Status = TemporalStatus.Artifact;

                    // on the revision doc
                    database.SetDocumentMetadata(revisionInfo.Key, transactionInformation,
                                                 TemporalMetadata.RavenDocumentTemporalStatus,
                                                 TemporalStatus.Artifact.ToString());
                }

                // Update the until date of the last revison prior to this one
                var lastRevision = history.Revisions.LastOrDefault(x => x.Key != newRevisionDoc.Key &&
                                                                        x.Status == TemporalStatus.Revision &&
                                                                        x.EffectiveStart < effective);
                if (lastRevision != null)
                {
                    // in the history
                    lastRevision.EffectiveUntil = effective;
                    lastRevision.AssertedUntil = now;

                    // on the revision doc
                    var md = new Dictionary<string, RavenJToken> {
                                                                     { TemporalMetadata.RavenDocumentTemporalEffectiveUntil, effective },
                                                                     { TemporalMetadata.RavenDocumentTemporalAssertedUntil, now }
                                                                 };
                    database.SetDocumentMetadata(lastRevision.Key, transactionInformation, md);
                }
            }

            // Update the history doc
            database.SaveTemporalHistoryFor(key, history, transactionInformation, historyEtag);

            // Reset the activation timer with each put.
            // This is so future revisions can become current without having to constantly poll.
            database.StartupTasks.OfType<TemporalActivator>().Single().ResetTimer(effective.UtcDateTime, now);

            // Return the revision number
            return revisionNumber;
        }
        public override ReadVetoResult AllowRead(string key, RavenJObject metadata, ReadOperation operation, TransactionInformation transactionInformation)
        {
            // always reset these
            _temporalVersioningEnabled.Value = false;
            _effectiveVersionKey.Value       = null;
            _now.Value = SystemTime.UtcNow;

            if (key == null)
            {
                return(ReadVetoResult.Allowed);
            }

            // This trigger is only for load operations
            if (operation != ReadOperation.Load)
            {
                return(ReadVetoResult.Allowed);
            }

            // Don't do anything if temporal versioning is inactive for this document type
            _temporalVersioningEnabled.Value = Database.IsTemporalVersioningEnabled(key, metadata);
            if (!_temporalVersioningEnabled.Value)
            {
                return(ReadVetoResult.Allowed);
            }

            // Only operate on current temporal documents
            var temporal = metadata.GetTemporalMetadata();

            if (temporal.Status != TemporalStatus.Current)
            {
                return(ReadVetoResult.Allowed);
            }

            // If an effective date was passed in, then use it.
            DateTimeOffset effective;
            var            headerValue = CurrentOperationContext.Headers.Value[TemporalMetadata.RavenTemporalEffective];

            if (headerValue == null || !DateTimeOffset.TryParse(headerValue, null, DateTimeStyles.RoundtripKind, out effective))
            {
                // If no effective data passed, return current data, as stored, effective now.
                temporal.Effective = _now.Value;
                return(ReadVetoResult.Allowed);
            }

            // Return the requested effective date in the metadata.
            temporal.Effective = effective;

            // If the current document is already in range, just return it
            if (temporal.EffectiveStart <= effective && effective < temporal.EffectiveUntil)
            {
                return(ReadVetoResult.Allowed);
            }

            // Load the history doc
            Etag historyEtag;
            var  history = Database.GetTemporalHistoryFor(key, transactionInformation, out historyEtag);

            // Find the revision that is effective at the date requested
            var effectiveRevisionInfo = history.Revisions.FirstOrDefault(x => x.Status == TemporalStatus.Revision &&
                                                                         x.EffectiveStart <= effective &&
                                                                         x.EffectiveUntil > effective);

            // Return nothing if there is no revision at the effective date
            if (effectiveRevisionInfo == null)
            {
                return(ReadVetoResult.Ignore);
            }

            // Hold on to the key so we can use it later in OnRead
            _effectiveVersionKey.Value = effectiveRevisionInfo.Key;
            return(ReadVetoResult.Allowed);
        }
        public override void OnPut(string key, RavenJObject document, RavenJObject metadata, TransactionInformation transactionInformation)
        {
            if (key == null)
                return;

            if (key.StartsWith("Raven/" + TemporalConstants.BundleName + "/Raven/"))
                throw new InvalidOperationException("Cannot version RavenDB system documents!");

            // Clear the config cache any time a new configuration is written.
            if (key.StartsWith("Raven/" + TemporalConstants.BundleName + "/"))
                TemporalVersioningUtil.ConfigCache.Clear();

            if (!Database.IsTemporalVersioningEnabled(key, metadata))
                return;

            using (Database.DisableAllTriggersForCurrentThread())
            {
                // When it's initially written, data is considered effective from the date specified to the end of time.
                var temporal = metadata.GetTemporalMetadata();
                temporal.EffectiveStart = temporal.Effective;
                temporal.EffectiveUntil = DateTimeOffset.MaxValue;

                // Set the asserted dates
                temporal.AssertedStart = _now.Value;
                temporal.AssertedUntil = DateTimeOffset.MaxValue;

                // See if the revision we're saving is current.
                var current = temporal.Effective <= _now.Value;

                // Don't save the requested date with the document
                temporal.Effective = null;

                if (!current)
                {
                    // When it's not current, then fetch the current one so we can put it back later.
                    // (This would not be necessary if Raven supported "instead of" triggers.)
                    _originalDocument.Value = Database.Get(key, transactionInformation);

                    // If this is the first revision and it's not current, then we don't want to keep a current doc at all.
                    _clearCurrent.Value = _originalDocument.Value == null;
                }

                // Always store this new data as a revision document
                var versionNumber = Database.PutRevision(key, document, metadata, transactionInformation, _now.Value);

                if (current)
                {
                    // When it's current, set the appropriate values the document that will be stored
                    temporal.Status = TemporalStatus.Current;
                    temporal.RevisionNumber = versionNumber;
                }
            }
        }
        public override void OnRead(string key, RavenJObject document, RavenJObject metadata, ReadOperation operation,
                                    TransactionInformation transactionInformation)
        {
            if (key == null)
            {
                return;
            }

            // If we're loading a revision directly, make sure we have set the rev number in the metadata
            var temporal = metadata.GetTemporalMetadata();

            if (key.Contains(TemporalConstants.TemporalKeySeparator))
            {
                temporal.RevisionNumber = int.Parse(key.Split('/').Last());
            }

            // Handle migration from nontemporal document
            if (temporal.Status == TemporalStatus.NonTemporal && _temporalVersioningEnabled.Value)
            {
                // Rewrite the document temporally.  We specifically do NOT disable triggers on this put.
                temporal.Effective = DateTimeOffset.MinValue;
                Database.Put(key, null, new RavenJObject(document), new RavenJObject(metadata), transactionInformation);

                // Fake out the current document for the return of this load.
                temporal.Status         = TemporalStatus.Current;
                temporal.RevisionNumber = 1;
                temporal.Effective      = _now.Value;
                temporal.EffectiveStart = DateTimeOffset.MinValue;
                temporal.EffectiveUntil = DateTimeOffset.MaxValue;
            }

            // If we didn't get a new effective revision key above, just return
            var effectiveRevisionKey = _effectiveVersionKey.Value;

            if (effectiveRevisionKey == null)
            {
                return;
            }

            _log.Debug("Temporally loading {0} instead of {1}", effectiveRevisionKey, key);

            using (Database.DisableAllTriggersForCurrentThread())
            {
                // Load the effective document
                var effectiveRevision = Database.Get(effectiveRevisionKey, transactionInformation);

                // Replace the resulting document
                foreach (var prop in document.Keys)
                {
                    document.Remove(prop);
                }
                var evDoc = effectiveRevision.DataAsJson;
                foreach (var prop in evDoc.Keys)
                {
                    document.Add(prop, evDoc[prop]);
                }

                // Replace the resulting metadata
                foreach (var prop in metadata.Keys)
                {
                    if (prop != TemporalMetadata.RavenTemporalEffective)
                    {
                        metadata.Remove(prop);
                    }
                }
                var evMetadata = effectiveRevision.Metadata;
                foreach (var prop in evMetadata.Keys)
                {
                    metadata.Add(prop, evMetadata[prop]);
                }

                // Send back the revision number also
                temporal.RevisionNumber = int.Parse(effectiveRevisionKey.Split('/').Last());
            }
        }
        public override void OnPut(string key, RavenJObject document, RavenJObject metadata, TransactionInformation transactionInformation)
        {
            if (key == null)
            {
                return;
            }

            if (key.StartsWith("Raven/" + TemporalConstants.BundleName + "/Raven/"))
            {
                throw new InvalidOperationException("Cannot version RavenDB system documents!");
            }

            // Clear the config cache any time a new configuration is written.
            if (key.StartsWith("Raven/" + TemporalConstants.BundleName + "/"))
            {
                TemporalVersioningUtil.ConfigCache.Clear();
            }

            if (!Database.IsTemporalVersioningEnabled(key, metadata))
            {
                return;
            }

            using (Database.DisableAllTriggersForCurrentThread())
            {
                // When it's initially written, data is considered effective from the date specified to the end of time.
                var temporal = metadata.GetTemporalMetadata();
                temporal.EffectiveStart = temporal.Effective;
                temporal.EffectiveUntil = DateTimeOffset.MaxValue;

                // Set the asserted dates
                temporal.AssertedStart = _now.Value;
                temporal.AssertedUntil = DateTimeOffset.MaxValue;

                // See if the revision we're saving is current.
                var current = temporal.Effective <= _now.Value;

                // Don't save the requested date with the document
                temporal.Effective = null;

                if (!current)
                {
                    // When it's not current, then fetch the current one so we can put it back later.
                    _originalDocument.Value = Database.Get(key, transactionInformation);

                    // If this is the first revision and it's not current, then we don't want to keep a current doc at all.
                    _clearCurrent.Value = _originalDocument.Value == null;

                    // If this is a future version, then the current doc might need to be updated
                    if (_originalDocument.Value != null)
                    {
                        var originalMetadata = _originalDocument.Value.Metadata.GetTemporalMetadata();
                        if (originalMetadata.EffectiveUntil > temporal.EffectiveStart)
                        {
                            originalMetadata.EffectiveUntil = temporal.EffectiveStart;
                            originalMetadata.AssertedUntil  = _now.Value;
                        }
                    }
                }

                // Always store this new data as a revision document
                var versionNumber = Database.PutRevision(key, document, metadata, transactionInformation, _now.Value);

                if (current)
                {
                    // When it's current, set the appropriate values the document that will be stored
                    temporal.Status         = TemporalStatus.Current;
                    temporal.RevisionNumber = versionNumber;
                }
            }
        }