private object GetSingleRelated(object model, string rltnName) { var resource = ModelRegistry.GetResource(model); var request = HttpRequestBuilder.GetRelated(resource.Id, resource.Type, rltnName); var response = HttpClient.SendAsync(request).GetAwaiter().GetResult(); HttpResponseListener.GetRelated(response.StatusCode, resource.Id, resource.Type, rltnName); if (response.StatusCode == HttpStatusCode.NotFound) { return(null); } response.CheckStatusCode(); // TODO: perhaps use a 3rd ResourceRoot with JToken Data to determine if array was erroneously returned var root = response.GetContentModel <ResourceRootSingle>(JsonSettings).GetAwaiter().GetResult(); var rltn = root.Data; if (rltn == null) { return(null); } var rltnModel = CreateResourceModel(rltn); Cache.Update(rltn.Id, rltnModel); return(rltnModel); }
public async Task Update <TModel>(TModel model) { // guard from kunckleheads if (model == null) { throw new ArgumentNullException(nameof(model)); } ThrowIfUnmanaged(model); var patch = BuildPatch(model); // if nothing was updated, no need to continue if (patch == null) { return; } var allModels = ModelRegistry.IncludedModelsCreate(model); foreach (var newModel in allModels) { var newResource = BuildModelResource(newModel); // Update the model instance in the argument var initialize = newModel.GetType().GetInitializeMethod(); initialize.Invoke(newModel, new object[] { newResource, this }); } var includes = allModels.Select(ModelRegistry.GetResource).ToArray(); var originalResource = ModelRegistry.GetResource(model); var root = ResourceRootSingle.FromResource(patch, includes); var request = await HttpRequestBuilder.UpdateResource(originalResource, root); var response = await HttpClient.SendAsync(request).ConfigureAwait(false); HttpResponseListener.UpdateResource(response.StatusCode, originalResource, root); response.CheckStatusCode(); if (response.StatusCode == HttpStatusCode.OK) { var responseRoot = await response.GetContentModel <ResourceRootSingle>(JsonSettings); // Update the model instance in the argument var initialize = typeof(TModel).GetInitializeMethod(); initialize.Invoke(model, new object[] { responseRoot.Data, this }); Cache.Update(responseRoot.Data.Id, model); } else if (response.StatusCode == HttpStatusCode.NoContent) { ModelRegistry.ApplyPatch(model, patch); } // create and cache includes await Task.WhenAll(allModels.Select(x => Task.Run(() => { var resource = ModelRegistry.GetResource(x); Cache.Update(resource.Id, x); }))); }
public Guid GetReferenceId <TModel>(TModel model, string attrName) { var relationships = ModelRegistry.GetResource(model)?.Relationships; if (relationships == null) { return(Guid.Empty); } return(relationships.TryGetValue(attrName, out var rltn) ? rltn.Data?.SelectToken("id").ToObject <Guid>() ?? Guid.Empty : Guid.Empty); }
public TRltn GetReference <TModel, TRltn>(TModel model, string rltnName) { ThrowIfDisposed(); // Assumption here is that GetReference is only used by HasOne to initialize itself var resource = ModelRegistry.GetResource(model); if (resource == null) { throw new UnmanagedModelException(model.GetType(), ModelRegistry.GetId(model)); } var relationships = resource.Relationships; if (relationships == null || !relationships.TryGetValue(rltnName, out var rltn)) { var related = GetSingleRelated(model, rltnName); rltn = new Relationship { Data = related != null ? JToken.FromObject(ModelRegistry.GetResource(related).ToResourceIdentifier()) : JValue.CreateNull() }; } // if we make it to here, 'rltn' has been set if (rltn == null) { throw new ModelMapException("Cannot find HasOne relationship", model.GetType(), ModelRegistry.GetId(model)); } var rltnData = rltn.Data; ResourceIdentifier rltnIdentifier; if (rltnData == null || rltnData.Type == JTokenType.Null) { return(default(TRltn)); } if (rltnData.Type == JTokenType.Object) { // TODO: I don't like that we're performing this conversion for every get rltnIdentifier = rltnData.ToObject <ResourceIdentifier>(); } else { throw new ModelMapException( $"Relationship {rltnName} mapped as [HasOne] but json relationship data was not an object", typeof(TModel), ModelRegistry.GetId(model)); } // calling Get<TRltn>(...) here will check the cache first, then go remote if necessary return(Get <TRltn>(rltnIdentifier.Id).GetAwaiter().GetResult()); }
public IEnumerable <Guid> GetRelationshipIds <TModel>(TModel model, string rltnName) { var relationships = ModelRegistry.GetResource(model)?.Relationships; if (relationships == null) { return(Array.Empty <Guid>()); } return(relationships.TryGetValue(rltnName, out var rltn) ? rltn.Data?.SelectTokens("[*].id") .Select(id => id.ToObject <Guid>()) .ToArray() ?? Array.Empty <Guid>() : Array.Empty <Guid>()); }
internal RemoteGenericBag(Session.Session session, object owner, string name, IEnumerable <TItem> items = null) : base(session, owner, name) { if (items == null) { // pull from owner resource var relationships = ModelRegistry.GetResource(owner).Relationships; if (relationships != null && relationships.TryGetValue(name, out var rltn)) { var ids = rltn.Data?.SelectTokens("[*].id"); ids.Each(id => Ids.Add(id.ToObject <Guid>())); } } else { items.Each(Add); } }
public void InitializeCollection(IRemoteCollection collection) { // TODO: abstract this into a collection loader/initializer // TODO: brute force this for now // TODO: don't run this task if the resource collection is empty/null! var resource = ModelRegistry.GetResource(collection.Owner); // determine if the cache is missing any models for this relationship var relationships = ModelRegistry.GetResource(collection.Owner).Relationships; if (relationships != null && relationships.TryGetValue(collection.Name, out var rltn)) { if (rltn?.Data.Type == JTokenType.Array && rltn.Data.ToObject <IEnumerable <ResourceIdentifier> >() .All(x => Cache.Retrieve <object>(x.Id) != null)) { return; } } var request = HttpRequestBuilder.GetRelated(resource.Id, resource.Type, collection.Name); var response = HttpClient.SendAsync(request).GetAwaiter().GetResult(); HttpResponseListener.GetRelated(response.StatusCode, resource.Id, resource.Type, collection.Name); if (response.StatusCode == HttpStatusCode.NotFound) { return; } response.CheckStatusCode(); // TODO: perhaps use a 3rd ResourceRoot with JToken Data to determine if object was erroneously returned var root = response.GetContentModel <ResourceRootCollection>(JsonSettings).GetAwaiter().GetResult(); var related = root.Data?.Select(x => { var rltnModel = CreateResourceModel(x); Cache.Update(x.Id, rltnModel); return(rltnModel); }) .ToArray(); collection.SetItems(related); }
private Resource BuildPatch(object model) { var originalResource = ModelRegistry.GetResource(model); var modelRtlns = ModelRegistry.GetRelationshipValues(model) ?? new Dictionary <string, Relationship>(); var originalRtlns = originalResource.Relationships ?? new Dictionary <string, Relationship>(); var patchRtlns = modelRtlns.Where(modelRltn => { originalRtlns.TryGetValue(modelRltn.Key, out var ogRtln); // JToken.DeepEquals does not work here return(!JsonComparer.Equals(ogRtln?.Data, modelRltn.Value?.Data)); }).ToDictionary(x => x.Key, x => x.Value); var modelAttrs = ModelRegistry.GetAttributeValues(model) ?? new JObject(); var originalAttrs = originalResource.Attributes ?? new JObject(); var modelMetas = ModelRegistry.GetMetaValues(model) ?? new JObject(); var originalMetas = originalResource.Meta ?? new JObject(); // Links are not patched var patchAttrs = (JObject)JsonDiff.ReducePatch(originalAttrs, modelAttrs); var patchMetas = (JObject)JsonDiff.ReducePatch(originalMetas, modelMetas); if (patchAttrs == null && !patchRtlns.Any() && patchMetas == null) { // Nothing to update return(null); } return(new Resource { Id = originalResource.Id, Type = originalResource.Type, Attributes = patchAttrs, Relationships = patchRtlns.Any() ? patchRtlns : null, Meta = patchMetas }); }
private async Task <TModel> Create <TModel>(Type rootModelType, object model) { ThrowIfDisposed(); // set Id if unset var modelId = model == null ? Guid.NewGuid() : ModelRegistry.GetOrCreateId(model); if (modelId == Guid.Empty) { modelId = Guid.NewGuid(); ModelRegistry.SetId(model, modelId); } // Create a new model instance if not already existing if (model == null) { model = Activator.CreateInstance <TModel>(); ModelRegistry.SetId(model, modelId); } if (ModelRegistry.IsManagedModel(model)) { throw new ManagedModelCreationException(model.GetType(), modelId); } // all unmanaged models in the object graph, including root var allModels = ModelRegistry.IncludedModelsCreate(model); foreach (var newModel in allModels) { var newResource = BuildModelResource(newModel); // Update the model instance in the argument var initialize = newModel.GetType().GetInitializeMethod(); initialize.Invoke(newModel, new object[] { newResource, this }); } var rootResource = ModelRegistry.GetResource(model); var includes = allModels.Where(x => x != model).Select(ModelRegistry.GetResource).ToArray(); if (Log.IsDebugEnabled()) { Log.Debug(() => $"preparing to POST {rootResource.Type}:{{{rootResource.Id}}}"); foreach (var include in includes) { Log.Debug(() => $"preparing to POST included {include.Type}:{{{include.Id}}}"); } } var root = ResourceRootSingle.FromResource(rootResource, includes); var request = await HttpRequestBuilder.CreateResource(root); var response = await HttpClient.SendAsync(request).ConfigureAwait(false); HttpResponseListener.CreateResource(response.StatusCode, root); response.CheckStatusCode(); if (response.StatusCode == HttpStatusCode.Created) { var responseRoot = await response.GetContentModel <ResourceRootSingle>(JsonSettings); // Update the model instance in the argument var initialize = rootModelType.GetInitializeMethod(); initialize.Invoke(model, new object[] { responseRoot.Data, this }); } // create and cache includes await Task.WhenAll(allModels.Select(x => Task.Run(() => { var resource = ModelRegistry.GetResource(x); Cache.Update(resource.Id, x); }))); return((TModel)model); }
public Guid GetId <TModel>(TModel model) { return(ModelRegistry.GetResource(model).Id); }