public static async Task <Customer> CreateCustomer(IClient client, IDraft <Customer> buildDraft) { var signInResult = (CustomerSignInResult)await client .ExecuteAsync(new SignUpCustomerCommand(buildDraft)); return(signInResult.Customer); }
/// <summary> /// Checks the arguments of the generator. /// </summary> /// <param name="draft">The draft.</param> /// <param name="basePath">The base path.</param> /// <param name="patches">The patches collection to add to.</param> /// <param name="inversePatches">The inverse patches to add to.</param> /// <returns>The adjusted base path.</returns> protected static string CheckArgumentsAndNormalizePath(IDraft draft, string?basePath, JsonPatchDocument patches, JsonPatchDocument inversePatches) { if (draft is null) { throw new ArgumentNullException(nameof(draft)); } basePath = $"/{(basePath ?? string.Empty).Trim('/')}"; if (patches is null) { throw new ArgumentNullException(nameof(patches)); } if (inversePatches is null) { throw new ArgumentNullException(nameof(inversePatches)); } if (draft.DraftState == null) { throw new PatchGenerationException(draft, "The draft has no draft state."); } return(basePath); }
/// <summary> /// Generates JSON Patches for a draft for changes and inverse changes and /// adds them to the specified JsonPatchDocuments. /// </summary> /// <param name="draft">The draft to process.</param> /// <param name="basePath">The base path for the patches.</param> /// <param name="patches">The patches.</param> /// <param name="inversePatches">The inverse patches.</param> public void Generate(IDraft draft, string?basePath, JsonPatchDocument patches, JsonPatchDocument inversePatches) { basePath = CheckArgumentsAndNormalizePath(draft, basePath, patches, inversePatches); // nothing to do. if (!draft.DraftState !.Changed) { return; } object source = draft.DraftState.GetOriginal <object>(); if (source is null) { throw new PatchGenerationException(draft, "The draft has no original state."); } IDictionary draftDictionary = (IDictionary)draft; IDictionary sourceDictionary = draft.DraftState.GetOriginal <IDictionary>(); if (sourceDictionary is null) { throw new PatchGenerationException(draft, "The draft has no original dictionary state."); } // Equals function that compares both values, but also checks whether the newValue might be just a // draft of the old value. bool DraftOrOriginalEquals(object oldValue, object newValue) => Equals(oldValue, newValue) || draft.DraftState.Scope.HasPatches.Contains(newValue) || (newValue is IDraft newDraft && newDraft.DraftState != null && Equals(oldValue, newDraft.DraftState !.GetOriginal <object>())); foreach (DictionaryEntry entry in sourceDictionary) { string key = entry.Key.ToString(); if (!draftDictionary.Contains(key)) { patches.Remove(basePath.PathJoin(key)); inversePatches.Add(basePath.PathJoin(key), sourceDictionary[entry.Key]); } else if (!DraftOrOriginalEquals(sourceDictionary[entry.Key], draftDictionary[entry.Key])) { patches.Replace(basePath.PathJoin(key), entry.Value); inversePatches.Replace(basePath.PathJoin(key), sourceDictionary[entry.Key]); } } foreach (DictionaryEntry entry in draftDictionary) { string key = entry.Key.ToString(); if (!sourceDictionary.Contains(entry.Key)) { patches.Add(basePath.PathJoin(key), entry.Value); inversePatches.Remove(basePath.PathJoin(key)); } } }
public static void Check(IDraft draft) { foreach (PropertyInfo prop in draft.GetType().GetProperties()) { if (prop.GetValue(draft) == null) { throw new PropertyNotFilledException(string.Format(propertyNotFilled, prop.Name), prop.Name); } } }
/// <summary> /// Generates JSON Patches for a draft for changes and inverse changes and /// adds them to the specified JsonPatchDocuments. /// </summary> /// <param name="draft">The draft to process.</param> /// <param name="basePath">The base path for the patches.</param> /// <param name="patches">The patches.</param> /// <param name="inversePatches">The inverse patches.</param> public void Generate(IDraft draft, string?basePath, JsonPatchDocument patches, JsonPatchDocument inversePatches) { basePath = CheckArgumentsAndNormalizePath(draft, basePath, patches, inversePatches); // nothing to do. if (!draft.DraftState !.Changed) { return; } object source = draft.DraftState.GetOriginal <object>(); if (source is null) { throw new PatchGenerationException(draft, "The draft has no original state."); } if (!(draft is IPropertyAccessors draftProperties)) { throw new PatchGenerationException(draft, "The draft has no property accessors."); } if (!(source is IPropertyAccessors sourceProperties)) { throw new PatchGenerationException(draft, "The source has no property accessors."); } foreach (string propertyName in draftProperties.PublicPropertyGetters.Keys) { object?oldValue = sourceProperties.PublicPropertyGetters[propertyName](); object?newValue = draftProperties.PublicPropertyGetters[propertyName](); // if the newValue is a draft and points to the same original, it's actually the same object in // a mutable sense and the patches will reflect that. Otherwise we would end up with very large patches // of the entire tree. if (Equals(oldValue, newValue) || draft.DraftState.Scope.HasPatches.Contains(newValue) || (newValue is IDraft newDraft && newDraft.DraftState != null && Equals(oldValue, newDraft.DraftState !.GetOriginal <object>()))) { continue; }
public static async Task <T> CreateResource <T>(IClient client, IDraft <T> buildDraft) where T : Resource <T> { return(await client .ExecuteAsync(new CreateCommand <T>(buildDraft))); }
public SignUpCustomerCommand(IDraft <Customer> entity) : base(entity) { }
public UpsertCommand(IDraft <T> entity) { this.Entity = entity; }
public UpsertCommand(IDraft <T> entity, IAdditionalParameters <T> additionalParameters) { this.Entity = entity; this.AdditionalParameters = additionalParameters; }
/// <summary> /// Generates JSON Patches for a draft for changes and inverse changes and /// adds them to the specified JsonPatchDocuments. /// </summary> /// <param name="draft">The draft to process.</param> /// <param name="basePath">The base path for the patches.</param> /// <param name="patches">The patches.</param> /// <param name="inversePatches">The inverse patches.</param> public void Generate(IDraft draft, string?basePath, JsonPatchDocument patches, JsonPatchDocument inversePatches) { basePath = CheckArgumentsAndNormalizePath(draft, basePath, patches, inversePatches); // nothing to do. if (!draft.DraftState !.Changed) { return; } IList draftList = (IList)draft; IList sourceList = draft.DraftState.GetOriginal <IList>(); if (sourceList is null) { throw new PatchGenerationException(draft, "The draft has no original list state."); } int commonHead = 0; int commonTail = 0; // Equals function that compares both values, but also checks whether the newValue might be just a // draft of the old value. bool DraftOrOriginalEquals(object oldValue, object newValue) => Equals(oldValue, newValue) || draft.DraftState.Scope.HasPatches.Contains(newValue) || (newValue is IDraft newDraft && newDraft.DraftState != null && Equals(oldValue, newDraft.DraftState.GetOriginal <object>())); // Find common head while (commonHead < sourceList.Count && commonHead < draftList.Count && DraftOrOriginalEquals(sourceList[commonHead], draftList[commonHead])) { commonHead++; } // Find common tail while (commonTail + commonHead < sourceList.Count && commonTail + commonHead < draftList.Count && DraftOrOriginalEquals(sourceList[sourceList.Count - 1 - commonTail], draftList[draftList.Count - 1 - commonTail])) { commonTail++; } if (commonHead + commonTail == draftList.Count) { // Trivial case, a block (one or more consecutive items) was removed // reverse the order so that there is never a problem with different implementations // (patch atomicity v.s. operation atomicity). for (int index = sourceList.Count - commonTail - 1; index >= commonHead; --index) { patches.Remove(basePath.PathJoin($"{index}")); inversePatches.Add(basePath.PathJoin($"{(index < draftList.Count ? index.ToString() : "-")}"), sourceList[index]); } return; } if (commonHead + commonTail == sourceList.Count) { // Trivial case, a block (one or more consecutive items) was added for (int index = commonHead; index < draftList.Count - commonTail; ++index) { patches.Add(basePath.PathJoin($"{(index < sourceList.Count ? index.ToString() : "-")}"), draftList[index]); inversePatches.Remove(basePath.PathJoin($"{index}")); } return; } // complex case, use lcs to determine list operations. var lcs = this.longestCommonSubsequence.Get( sourceList, draftList, commonHead, sourceList.Count - commonTail - commonHead, commonHead, draftList.Count - commonTail - commonHead, DraftOrOriginalEquals); int lcsIndex = lcs.Length - 1; // reverse the order so that there is never a problem with different implementations // (patch atomicity v.s. operation atomicity). for (int index = sourceList.Count - commonTail - 1; index >= commonHead; --index) { if (lcsIndex < 0 || !Equals(sourceList[index], lcs[lcsIndex])) { patches.Remove(basePath.PathJoin($"{index}")); inversePatches.Add(basePath.PathJoin($"{(index < draftList.Count ? index.ToString() : "-")}"), sourceList[index]); } else { --lcsIndex; } } lcsIndex = 0; for (int index = commonHead; index < draftList.Count - commonTail; ++index) { if (lcsIndex >= lcs.Length || !DraftOrOriginalEquals(lcs[lcsIndex], draftList[index])) { patches.Add(basePath.PathJoin($"{(index < lcs.Length ? index.ToString() : "-")}"), draftList[index]); inversePatches.Remove(basePath.PathJoin($"{index}")); } else { ++lcsIndex; } } }
public CustomObjectUpsertCommand(IDraft <CustomObject <T> > entity) : base(entity) { }
public SignUpCommand(IDraft <T> entity) { this.Entity = entity; }
public DraftController(IDraft draft) { _draft = draft; }
public static async Task <CustomObject <T> > CreateCustomObject <T>(IClient client, IDraft <CustomObject <T> > buildDraft) { return(await client .ExecuteAsync(new CustomObjectUpsertCommand <T>(buildDraft))); }
public CreateCommand(IDraft <T> entity) { this.Entity = entity; }