예제 #1
0
파일: Session.cs 프로젝트: engenb/Argo
        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);
        }
예제 #2
0
파일: Session.cs 프로젝트: engenb/Argo
        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);
            })));
        }
예제 #3
0
파일: Session.cs 프로젝트: engenb/Argo
        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);
        }
예제 #4
0
파일: Session.cs 프로젝트: engenb/Argo
        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());
        }
예제 #5
0
파일: Session.cs 프로젝트: engenb/Argo
        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>());
        }
예제 #6
0
 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);
     }
 }
예제 #7
0
파일: Session.cs 프로젝트: engenb/Argo
        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);
        }
예제 #8
0
파일: Session.cs 프로젝트: engenb/Argo
        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
            });
        }
예제 #9
0
파일: Session.cs 프로젝트: engenb/Argo
        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);
        }
예제 #10
0
파일: Session.cs 프로젝트: engenb/Argo
 public Guid GetId <TModel>(TModel model)
 {
     return(ModelRegistry.GetResource(model).Id);
 }