public void CanUseToISOString() { var date = DateTime.UtcNow; var dateOffset = DateTime.Now.AddMilliseconds(100); var testObject = new CustomType { Date = date, DateOffset = dateOffset }; var doc = RavenJObject.FromObject(testObject); var patch = new ScriptedPatchRequest() { Script = @" this.DateOutput = new Date(this.Date).toISOString(); this.DateOffsetOutput = new Date(this.DateOffset).toISOString(); " }; using (var scope = new DefaultScriptedJsonPatcherOperationScope()) { var scriptedJsonPatcher = new ScriptedJsonPatcher(); var result = scriptedJsonPatcher.Apply(scope, doc, patch); var dateOutput = result.Value <string>("DateOutput"); var dateOffsetOutput = result.Value <string>("DateOffsetOutput"); // With the custom fixes to Jint, these tests now pass (RavenDB-1536) Assert.Equal(date.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"), dateOutput); Assert.Equal(dateOffset.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ"), dateOffsetOutput); } }
public void PatcherCanOutputObjectsCorrectly() { var doc = RavenJObject.Parse("{}"); const string script = @"output(undefined); output(true); output(2); output(2.5); output('string'); output(null); output([2, 'c']); output({'a': 'c', 'f': { 'x' : 2}});" ; var patch = new ScriptedPatchRequest() { Script = script }; using (var scope = new DefaultScriptedJsonPatcherOperationScope()) { var patcher = new ScriptedJsonPatcher(); patcher.Apply(scope, doc, patch); Assert.Equal(8, patcher.Debug.Count); Assert.Equal("undefined", patcher.Debug[0]); Assert.Equal("True", patcher.Debug[1]); Assert.Equal("2", patcher.Debug[2]); Assert.Equal("2.5", patcher.Debug[3]); Assert.Equal("string", patcher.Debug[4]); Assert.Equal("null", patcher.Debug[5]); Assert.Equal("[2,\"c\"]", patcher.Debug[6]); Assert.Equal("{\"a\":\"c\",\"f\":{\"x\":2}}", patcher.Debug[7]); } }
public void CanProcess() { var document = new RavenJObject { { "Data", new RavenJObject { { "Title", "Hi" } } } }; const string name = @"Raven.Tests.Patching.x2js.js"; var manifestResourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name); var code = new StreamReader(manifestResourceStream).ReadToEnd(); var jsonPatcher = new ScriptedJsonPatcher(); using (var scope = new DefaultScriptedJsonPatcherOperationScope()) { scope.CustomFunctions = new JsonDocument { DataAsJson = new RavenJObject { { "Functions", code } } }; jsonPatcher.Apply(scope, document, new ScriptedPatchRequest { Script = "this.Xml = js2x(this.Data);" }); } }
public Tuple <PatchResultData, List <string> > ApplyPatch(string docId, Etag etag, ScriptedPatchRequest patchExisting, ScriptedPatchRequest patchDefault, RavenJObject defaultMetadata, TransactionInformation transactionInformation, bool debugMode = false, IEnumerable <string> participatingIds = null) { ScriptedJsonPatcher scriptedJsonPatcher = null; DefaultScriptedJsonPatcherOperationScope scope = null; try { var applyPatchInternal = ApplyPatchInternal(docId, etag, transactionInformation, jsonDoc => { scope = scope ?? new DefaultScriptedJsonPatcherOperationScope(Database, debugMode); scriptedJsonPatcher = new ScriptedJsonPatcher(Database); return(scriptedJsonPatcher.Apply(scope, jsonDoc.ToJson(), patchExisting, jsonDoc.SerializedSizeOnDisk, jsonDoc.Key)); }, () => { if (patchDefault == null) { return(null); } scope = scope ?? new DefaultScriptedJsonPatcherOperationScope(Database, debugMode); scriptedJsonPatcher = new ScriptedJsonPatcher(Database); var jsonDoc = new RavenJObject(); jsonDoc[Constants.Metadata] = defaultMetadata.CloneToken() ?? new RavenJObject(); return(scriptedJsonPatcher.Apply(scope, jsonDoc, patchDefault, 0, docId)); }, () => { if (scope == null) { return(null); } return(scope .GetPutOperations() .ToList()); }, () => { if (scope == null) { return(null); } return(scope.DebugActions); }, debugMode, participatingIds: participatingIds); return(Tuple.Create(applyPatchInternal, scriptedJsonPatcher == null ? new List <string>() : scriptedJsonPatcher.Debug)); } finally { if (scope != null) { scope.Dispose(); } } }
public void CanLoadPutDocumentsMultipleTimesInPatcher() { using (var store = NewDocumentStore()) { using (var s = store.OpenSession()) { s.Store(new OpCounter(), "opCounter"); s.SaveChanges(); } var patcher = new ScriptedJsonPatcher(store.DocumentDatabase); using (var scope = new ScriptedIndexResultsJsonPatcherScope(store.DocumentDatabase, new HashSet <string> { "dogs" })) { patcher.Apply(scope, new RavenJObject(), new ScriptedPatchRequest { Script = @"var opCounterId = 'opCounter'; var opCounter = LoadDocument(opCounterId) || {}; opCounter.Index++; PutDocument(opCounterId, opCounter); opCounter = LoadDocument(opCounterId) opCounter.Deletes++; PutDocument(opCounterId, opCounter); " }); store.DocumentDatabase.TransactionalStorage.Batch(accessor => { foreach (var operation in scope.GetOperations()) { switch (operation.Type) { case ScriptedJsonPatcher.OperationType.Put: store.DocumentDatabase.Documents.Put(operation.Document.Key, operation.Document.Etag, operation.Document.DataAsJson, operation.Document.Metadata, null); break; case ScriptedJsonPatcher.OperationType.Delete: store.DocumentDatabase.Documents.Delete(operation.DocumentKey, null, null); break; default: throw new ArgumentOutOfRangeException("operation.Type"); } } }); } using (var s = store.OpenSession()) { var opCounter = s.Load <OpCounter>("opCounter"); Assert.Equal(1, opCounter.Deletes); Assert.Equal(1, opCounter.Index); } } }
public void CreateDocumentShouldThrowIfSpecifiedJsonIsNullOrEmptyString() { var doc = RavenJObject.FromObject(test); var advancedJsonPatcher = new ScriptedJsonPatcher(); var x = Assert.Throws <InvalidOperationException>(() => advancedJsonPatcher.Apply(doc, new ScriptedPatchRequest { Script = @"PutDocument('Items/1', null);" })); Assert.Contains("Created document cannot be null or empty. Document key: 'Items/1'", x.InnerException.Message); x = Assert.Throws <InvalidOperationException>(() => advancedJsonPatcher.Apply(doc, new ScriptedPatchRequest { Script = @"PutDocument('Items/1', null, null);" })); Assert.Contains("Created document cannot be null or empty. Document key: 'Items/1'", x.InnerException.Message); }
public void LongStringRejected() { var doc = RavenJObject.FromObject(test); var advancedJsonPatcher = new ScriptedJsonPatcher(); Assert.Throws <NotSupportedException>(() => advancedJsonPatcher.Apply(doc, new ScriptedPatchRequest { Script = new string('a', 8193) })); }
public void DefiningFunctionForbidden() { var doc = RavenJObject.FromObject(test); var advancedJsonPatcher = new ScriptedJsonPatcher(); Assert.Throws <NotSupportedException>(() => advancedJsonPatcher.Apply(doc, new ScriptedPatchRequest { Script = "function a() { a(); }" })); }
public void CannotUseWhile() { var doc = RavenJObject.FromObject(test); var advancedJsonPatcher = new ScriptedJsonPatcher(); Assert.Throws <NotSupportedException>(() => advancedJsonPatcher.Apply(doc, new ScriptedPatchRequest { Script = "while(true) {}" })); }
public void CannotUseInfiniteLoop() { var doc = RavenJObject.FromObject(test); var advancedJsonPatcher = new ScriptedJsonPatcher(); var x = Assert.Throws <InvalidOperationException>(() => advancedJsonPatcher.Apply(doc, new ScriptedPatchRequest { Script = "while(true) {}" })); Assert.Contains("Too many steps in script", x.Message); }
public void CreateDocumentShouldThrowInvalidEtagException() { var doc = RavenJObject.FromObject(test); var advancedJsonPatcher = new ScriptedJsonPatcher(); var x = Assert.Throws <InvalidOperationException>(() => advancedJsonPatcher.Apply(doc, new ScriptedPatchRequest { Script = @"PutDocument('Items/1', { Property: 1}, {'@etag': 'invalid-etag' });" })); Assert.Contains("Invalid ETag value 'invalid-etag' for document 'Items/1'", x.InnerException.Message); }
public void CanOutputDebugInformation() { var doc = RavenJObject.FromObject(test); var advancedJsonPatcher = new ScriptedJsonPatcher(); advancedJsonPatcher.Apply(doc, new ScriptedPatchRequest { Script = "output(this.Id)" }); Assert.Equal("someId", advancedJsonPatcher.Debug[0]); }
public void ShouldWork() { var scriptedJsonPatcher = new ScriptedJsonPatcher(); var result = scriptedJsonPatcher.Apply(new RavenJObject { { "Val", double.NaN } }, new ScriptedPatchRequest { Script = @"this.Finite = isFinite(this.Val);" }); Assert.False(result.Value <bool>("Finite")); }
public void CanRemoveFromCollectionByCondition() { var doc = RavenJObject.FromObject(test); var advancedJsonPatcher = new ScriptedJsonPatcher(); var resultJson = advancedJsonPatcher.Apply(doc, new ScriptedPatchRequest { Script = @" this.Comments.RemoveWhere(function(el) {return el == 'seven';}); " }); var result = JsonConvert.DeserializeObject <CustomType>(resultJson.ToString()); Assert.Equal(new[] { "one", "two" }.ToList(), result.Comments); }
public void ScriptPatchShouldGenerateNiceException() { using (var store = NewDocumentStore()) { using (var session = store.OpenSession()) { session.Store(new SimpleUser { FirstName = "John", LastName = "Smith" }); session.SaveChanges(); } store .DatabaseCommands .Put( Constants.RavenJavascriptFunctions, null, RavenJObject.FromObject(new { Functions = @"exports.a = function(value) { return b(value); }; exports.b = function(v) { return c(v); } exports.c = function(v) { throw 'oops'; } " }), new RavenJObject()); WaitForIndexing(store); var patcher = new ScriptedJsonPatcher(store.SystemDatabase); using (var scope = new ScriptedIndexResultsJsonPatcherScope(store.SystemDatabase, new HashSet <string>())) { var e = Assert.Throws <InvalidOperationException>(() => patcher.Apply(scope, new RavenJObject(), new ScriptedPatchRequest { Script = @"var s = 1234; a(s);" })); Assert.Equal(@"Unable to execute JavaScript: var s = 1234; a(s); Error: oops Stacktrace: [email protected]:3 [email protected]:2 [email protected]:1 [email protected]:2 anonymous [email protected]:1", e.Message); } } }
public void CanOutputDebugInformation() { var doc = RavenJObject.FromObject(test); using (var scope = new DefaultScriptedJsonPatcherOperationScope()) { var advancedJsonPatcher = new ScriptedJsonPatcher(); advancedJsonPatcher.Apply(scope, doc, new ScriptedPatchRequest { Script = "output(this.Id)" }); Assert.Equal("someId", advancedJsonPatcher.Debug[0]); } }
public void CanUseSplit() { var doc = RavenJObject.Parse("{\"Email\":'*****@*****.**'}"); const string script = @" this.Parts = this.Email.split('@');"; var patch = new ScriptedPatchRequest() { Script = script, }; var scriptedJsonPatcher = new ScriptedJsonPatcher(); var result = scriptedJsonPatcher.Apply(doc, patch); Assert.Equal(result["Parts"].Value <RavenJArray>()[0], "somebody"); Assert.Equal(result["Parts"].Value <RavenJArray>()[1], "somewhere.com"); }
public void ShouldWork() { var scriptedJsonPatcher = new ScriptedJsonPatcher(); using (var scope = new DefaultScriptedJsonPatcherOperationScope()) { var result = scriptedJsonPatcher.Apply(scope, new RavenJObject { { "Val", double.NaN } }, new ScriptedPatchRequest { Script = @"this.Finite = isFinite(this.Val);" }); Assert.False(result.Value <bool>("Finite")); } }
public Tuple <PatchResultData, List <string> > ApplyPatch(string docId, Etag etag, ScriptedPatchRequest patch, TransactionInformation transactionInformation, bool debugMode = false) { ScriptedJsonPatcher scriptedJsonPatcher = null; DefaultScriptedJsonPatcherOperationScope scope = null; try { var applyPatchInternal = ApplyPatchInternal(docId, etag, transactionInformation, jsonDoc => { scope = new DefaultScriptedJsonPatcherOperationScope(Database, debugMode); scriptedJsonPatcher = new ScriptedJsonPatcher(Database); return(scriptedJsonPatcher.Apply(scope, jsonDoc.ToJson(), patch, jsonDoc.SerializedSizeOnDisk, jsonDoc.Key)); }, () => null, () => { if (scope == null) { return(null); } return(scope .GetPutOperations() .ToList()); }, () => { if (scope == null) { return(null); } return(scope.DebugActions); }, debugMode); return(Tuple.Create(applyPatchInternal, scriptedJsonPatcher == null ? new List <string>() : scriptedJsonPatcher.Debug)); } finally { if (scope != null) { scope.Dispose(); } } }
public void ScriptedIndexResultsPatcherShouldNotPatchDocumentsThatAreIndirectlyReferencedByIndexRaw() { using (var store = NewDocumentStore(activeBundles: "ScriptedIndexResults")) { new CustomerOrderTotal().Execute(store); using (var session = store.OpenSession()) { session.Store(new Customer { Name = "Customer1", StoredTotal = 100, Id = "customers/1" }); session.SaveChanges(); session.Store(new Order { Customers = new List <string> { "customers/1" }, OrderItems = 10 }); session.SaveChanges(); } WaitForIndexing(store); var patcher = new ScriptedJsonPatcher(store.SystemDatabase); using (var scope = new ScriptedIndexResultsJsonPatcherScope(store.SystemDatabase, new HashSet <string> { "Orders" })) { var e = Assert.Throws <InvalidOperationException>(() => patcher.Apply(scope, new RavenJObject(), new ScriptedPatchRequest { Script = @"var customer = LoadDocument('customers/1') if(customer == null) return; if(customer.StoredTotal != this.TotalOrderItems){ customer.StoredTotal = this.TotalOrderItems; PutDocument('customers/1', customer); }" })); Assert.Equal("Cannot PUT document 'customers/1' to prevent infinite indexing loop. Avoid modifying documents that could be indirectly referenced by index.", e.InnerException.Message); } } }
public void CannotUseInfiniteLoop() { var doc = RavenJObject.FromObject(test); var advancedJsonPatcher = new ScriptedJsonPatcher(); var x = Assert.Throws <InvalidOperationException>(() => { using (var scope = new DefaultScriptedJsonPatcherOperationScope()) { advancedJsonPatcher.Apply(scope, doc, new ScriptedPatchRequest { Script = "while(true) {}" }); } }); Assert.Contains("Unable to execute JavaScript", x.Message); var inner = x.InnerException as StatementsCountOverflowException; Assert.NotNull(inner); Assert.Equal("The maximum number of statements executed have been reached.", inner.Message); }
public override void Dispose() { var patcher = new ScriptedJsonPatcher(database); using (var scope = new ScriptedIndexResultsJsonPatcherScope(database, forEntityNames)) { if (string.IsNullOrEmpty(scriptedIndexResults.DeleteScript) == false) { foreach (var kvp in removed) { foreach (var entry in kvp.Value) { patcher.Apply(scope, entry, new ScriptedPatchRequest { Script = scriptedIndexResults.DeleteScript, Values = { { "key", kvp.Key } } }); if (Log.IsDebugEnabled && patcher.Debug.Count > 0) { Log.Debug("Debug output for doc: {0} for index {1} (delete):\r\n.{2}", kvp.Key, scriptedIndexResults.Id, string.Join("\r\n", patcher.Debug)); patcher.Debug.Clear(); } } } } if (string.IsNullOrEmpty(scriptedIndexResults.IndexScript) == false) { foreach (var kvp in created) { try { foreach (var entry in kvp.Value) { patcher.Apply(scope, entry, new ScriptedPatchRequest { Script = scriptedIndexResults.IndexScript, Values = { { "key", kvp.Key } } }); } } catch (Exception e) { Log.Warn("Could not apply index script " + scriptedIndexResults.Id + " to index result with key: " + kvp.Key, e); } finally { if (Log.IsDebugEnabled && patcher.Debug.Count > 0) { Log.Debug("Debug output for doc: {0} for index {1} (index):\r\n.{2}", kvp.Key, scriptedIndexResults.Id, string.Join("\r\n", patcher.Debug)); patcher.Debug.Clear(); } } } } database.TransactionalStorage.Batch(accessor => { foreach (var operation in scope.GetOperations()) { switch (operation.Type) { case ScriptedJsonPatcher.OperationType.Put: database.Documents.Put(operation.Document.Key, operation.Document.Etag, operation.Document.DataAsJson, operation.Document.Metadata, null); break; case ScriptedJsonPatcher.OperationType.Delete: database.Documents.Delete(operation.DocumentKey, null, null); break; default: throw new ArgumentOutOfRangeException("operation.Type"); } } }); } }
public override void Dispose() { bool shouldRetry = false; int retries = 128; Random rand = null; do { using (shouldRetry ? database.TransactionalStorage.DisableBatchNesting() : null) { var patcher = new ScriptedJsonPatcher(database); using (var scope = new ScriptedIndexResultsJsonPatcherScope(database, forEntityNames)) { if (string.IsNullOrEmpty(scriptedIndexResults.DeleteScript) == false) { foreach (var kvp in removed) { foreach (var entry in kvp.Value) { try { patcher.Apply(scope, entry, new ScriptedPatchRequest { Script = scriptedIndexResults.DeleteScript, Values = { { "key", kvp.Key } } }); } catch (Exception e) { Log.WarnException("Could not apply delete script " + scriptedIndexResults.Id + " to index result with key: " + kvp.Key, e); } finally { if (Log.IsDebugEnabled && patcher.Debug.Count > 0) { Log.Debug("Debug output for doc: {0} for index {1} (delete):\r\n.{2}", kvp.Key, scriptedIndexResults.Id, string.Join("\r\n", patcher.Debug)); patcher.Debug.Clear(); } } } } } if (string.IsNullOrEmpty(scriptedIndexResults.IndexScript) == false) { foreach (var kvp in created) { try { foreach (var entry in kvp.Value) { patcher.Apply(scope, entry, new ScriptedPatchRequest { Script = scriptedIndexResults.IndexScript, Values = { { "key", kvp.Key } } }); } } catch (Exception e) { Log.WarnException("Could not apply index script " + scriptedIndexResults.Id + " to index result with key: " + kvp.Key, e); } finally { if (Log.IsDebugEnabled && patcher.Debug.Count > 0) { Log.Debug("Debug output for doc: {0} for index {1} (index):\r\n.{2}", kvp.Key, scriptedIndexResults.Id, string.Join("\r\n", patcher.Debug)); patcher.Debug.Clear(); } } } } try { database.TransactionalStorage.Batch(accessor => { foreach (var operation in scope.GetOperations()) { switch (operation.Type) { case ScriptedJsonPatcher.OperationType.Put: if (Log.IsDebugEnabled) { Log.Debug("Perform PUT on {0} for scripted index results {1}", operation.Document.Key, scriptedIndexResults.Id); } database.Documents.Put(operation.Document.Key, operation.Document.Etag, operation.Document.DataAsJson, operation.Document.Metadata, null); break; case ScriptedJsonPatcher.OperationType.Delete: if (Log.IsDebugEnabled) { Log.Debug("Perform DELETE on {0} for scripted index results {1}", operation.DocumentKey, scriptedIndexResults.Id); } database.Documents.Delete(operation.DocumentKey, null, null); break; default: throw new ArgumentOutOfRangeException("operation.Type: " + operation.Type); } } }); shouldRetry = false; } catch (ConcurrencyException ex) { if (scriptedIndexResults.RetryOnConcurrencyExceptions && retries-- > 0) { shouldRetry = true; if (rand == null) { rand = new Random(); } if (Log.IsDebugEnabled) { Log.DebugException(string.Format("Applying PUT/DELETE for scripted index results {0} failed with concurrency exception. Retrying", scriptedIndexResults.Id), ex); } Thread.Sleep(rand.Next(5, Math.Max(retries * 2, 10))); continue; } if (Log.IsDebugEnabled) { Log.DebugException(string.Format("Applying PUT/DELETE for scripted index results {0} failed with concurrency exception {1} times.", scriptedIndexResults.Id, 128 - retries + 1), ex); } throw; } } } } while (shouldRetry); }