コード例 #1
0
        private OperationContainer ParseForDeleteResourceOperation(AtomicOperationObject operation, OperationKind kind)
        {
            AssertElementHasType(operation.Ref, "ref");
            AssertElementHasIdOrLid(operation.Ref, "ref", true);

            ResourceContext primaryResourceContext = GetExistingResourceContext(operation.Ref.Type);

            AssertCompatibleId(operation.Ref, primaryResourceContext.IdentityType);

            IIdentifiable primaryResource = ResourceFactory.CreateInstance(primaryResourceContext.ResourceType);

            primaryResource.StringId = operation.Ref.Id;
            primaryResource.LocalId  = operation.Ref.Lid;

            var request = new JsonApiRequest
            {
                Kind            = EndpointKind.AtomicOperations,
                BasePath        = _request.BasePath,
                PrimaryId       = primaryResource.StringId,
                PrimaryResource = primaryResourceContext,
                OperationKind   = kind
            };

            return(new OperationContainer(kind, primaryResource, new TargetedFields(), request));
        }
コード例 #2
0
        private OperationContainer DeserializeOperation(AtomicOperationObject operation)
        {
            _targetedFields.Attributes.Clear();
            _targetedFields.Relationships.Clear();

            AssertHasNoHref(operation);

            OperationKind kind = GetOperationKind(operation);

            switch (kind)
            {
            case OperationKind.CreateResource:
            case OperationKind.UpdateResource:
            {
                return(ParseForCreateOrUpdateResourceOperation(operation, kind));
            }

            case OperationKind.DeleteResource:
            {
                return(ParseForDeleteResourceOperation(operation, kind));
            }
            }

            bool requireToManyRelationship = kind == OperationKind.AddToRelationship || kind == OperationKind.RemoveFromRelationship;

            return(ParseForRelationshipOperation(operation, kind, requireToManyRelationship));
        }
コード例 #3
0
        private OperationKind GetOperationKind(AtomicOperationObject operation)
        {
            switch (operation.Code)
            {
            case AtomicOperationCode.Add:
            {
                if (operation.Ref != null && operation.Ref.Relationship == null)
                {
                    throw new JsonApiSerializationException("The 'ref.relationship' element is required.", null,
                                                            atomicOperationIndex: AtomicOperationIndex);
                }

                return(operation.Ref == null ? OperationKind.CreateResource : OperationKind.AddToRelationship);
            }

            case AtomicOperationCode.Update:
            {
                return(operation.Ref?.Relationship != null ? OperationKind.SetRelationship : OperationKind.UpdateResource);
            }

            case AtomicOperationCode.Remove:
            {
                if (operation.Ref == null)
                {
                    throw new JsonApiSerializationException("The 'ref' element is required.", null, atomicOperationIndex: AtomicOperationIndex);
                }

                return(operation.Ref.Relationship != null ? OperationKind.RemoveFromRelationship : OperationKind.DeleteResource);
            }
            }

            throw new NotSupportedException($"Unknown operation code '{operation.Code}'.");
        }
コード例 #4
0
        private void AssertSameIdentityInRefData(AtomicOperationObject operation, ResourceIdentifierObject resourceIdentifierObject)
        {
            if (operation.Ref.Id != null && resourceIdentifierObject.Id != null && resourceIdentifierObject.Id != operation.Ref.Id)
            {
                throw new JsonApiSerializationException("Resource ID mismatch between 'ref.id' and 'data.id' element.",
                                                        $"Expected resource with ID '{operation.Ref.Id}' in 'data.id', instead of '{resourceIdentifierObject.Id}'.",
                                                        atomicOperationIndex: AtomicOperationIndex);
            }

            if (operation.Ref.Lid != null && resourceIdentifierObject.Lid != null && resourceIdentifierObject.Lid != operation.Ref.Lid)
            {
                throw new JsonApiSerializationException("Resource local ID mismatch between 'ref.lid' and 'data.lid' element.",
                                                        $"Expected resource with local ID '{operation.Ref.Lid}' in 'data.lid', instead of '{resourceIdentifierObject.Lid}'.",
                                                        atomicOperationIndex: AtomicOperationIndex);
            }

            if (operation.Ref.Id != null && resourceIdentifierObject.Lid != null)
            {
                throw new JsonApiSerializationException("Resource identity mismatch between 'ref.id' and 'data.lid' element.",
                                                        $"Expected resource with ID '{operation.Ref.Id}' in 'data.id', instead of '{resourceIdentifierObject.Lid}' in 'data.lid'.",
                                                        atomicOperationIndex: AtomicOperationIndex);
            }

            if (operation.Ref.Lid != null && resourceIdentifierObject.Id != null)
            {
                throw new JsonApiSerializationException("Resource identity mismatch between 'ref.lid' and 'data.id' element.",
                                                        $"Expected resource with local ID '{operation.Ref.Lid}' in 'data.lid', instead of '{resourceIdentifierObject.Id}' in 'data.id'.",
                                                        atomicOperationIndex: AtomicOperationIndex);
            }
        }
コード例 #5
0
 private void AssertHasNoHref(AtomicOperationObject operation)
 {
     if (operation.Href != null)
     {
         throw new JsonApiSerializationException("Usage of the 'href' element is not supported.", null, atomicOperationIndex: AtomicOperationIndex);
     }
 }
コード例 #6
0
        private OperationContainer ParseForCreateOrUpdateResourceOperation(AtomicOperationObject operation, OperationKind kind)
        {
            ResourceObject resourceObject = GetRequiredSingleDataForResourceOperation(operation);

            AssertElementHasType(resourceObject, "data");
            AssertElementHasIdOrLid(resourceObject, "data", kind != OperationKind.CreateResource);

            ResourceContext primaryResourceContext = GetExistingResourceContext(resourceObject.Type);

            AssertCompatibleId(resourceObject, primaryResourceContext.IdentityType);

            if (operation.Ref != null)
            {
                // For resource update, 'ref' is optional. But when specified, it must match with 'data'.

                AssertElementHasType(operation.Ref, "ref");
                AssertElementHasIdOrLid(operation.Ref, "ref", true);

                ResourceContext resourceContextInRef = GetExistingResourceContext(operation.Ref.Type);

                if (resourceContextInRef != primaryResourceContext)
                {
                    throw new JsonApiSerializationException("Resource type mismatch between 'ref.type' and 'data.type' element.",
                                                            $"Expected resource of type '{resourceContextInRef.PublicName}' in 'data.type', instead of '{primaryResourceContext.PublicName}'.",
                                                            atomicOperationIndex: AtomicOperationIndex);
                }

                AssertSameIdentityInRefData(operation, resourceObject);
            }

            var request = new JsonApiRequest
            {
                Kind = EndpointKind.AtomicOperations,
#pragma warning disable CS0618 // Type or member is obsolete
                BasePath = _request.BasePath,
#pragma warning restore CS0618 // Type or member is obsolete
                PrimaryResource = primaryResourceContext,
                OperationKind   = kind
            };

            _request.CopyFrom(request);

            IIdentifiable primaryResource = ParseResourceObject(operation.SingleData);

            _resourceDefinitionAccessor.OnDeserialize(primaryResource);

            request.PrimaryId = primaryResource.StringId;
            _request.CopyFrom(request);

            var targetedFields = new TargetedFields
            {
                Attributes    = _targetedFields.Attributes.ToHashSet(),
                Relationships = _targetedFields.Relationships.ToHashSet()
            };

            AssertResourceIdIsNotTargeted(targetedFields);

            return(new OperationContainer(kind, primaryResource, targetedFields, request));
        }
コード例 #7
0
        private OperationContainer ParseForRelationshipOperation(AtomicOperationObject operation, OperationKind kind,
                                                                 bool requireToMany)
        {
            AssertElementHasType(operation.Ref, "ref");
            AssertElementHasIdOrLid(operation.Ref, "ref", true);

            var primaryResourceContext = GetExistingResourceContext(operation.Ref.Type);

            AssertCompatibleId(operation.Ref, primaryResourceContext.IdentityType);

            var primaryResource = ResourceFactory.CreateInstance(primaryResourceContext.ResourceType);

            primaryResource.StringId = operation.Ref.Id;
            primaryResource.LocalId  = operation.Ref.Lid;

            var relationship = GetExistingRelationship(operation.Ref, primaryResourceContext);

            if (requireToMany && relationship is HasOneAttribute)
            {
                throw new JsonApiSerializationException(
                          $"Only to-many relationships can be targeted in '{operation.Code.ToString().Camelize()}' operations.",
                          $"Relationship '{operation.Ref.Relationship}' must be a to-many relationship.",
                          atomicOperationIndex: AtomicOperationIndex);
            }

            var secondaryResourceContext = ResourceContextProvider.GetResourceContext(relationship.RightType);

            var request = new JsonApiRequest
            {
                Kind              = EndpointKind.AtomicOperations,
                BasePath          = _request.BasePath,
                PrimaryId         = primaryResource.StringId,
                PrimaryResource   = primaryResourceContext,
                SecondaryResource = secondaryResourceContext,
                Relationship      = relationship,
                IsCollection      = relationship is HasManyAttribute,
                OperationKind     = kind
            };

            _request.CopyFrom(request);

            _targetedFields.Relationships.Add(relationship);

            ParseDataForRelationship(relationship, secondaryResourceContext, operation, primaryResource);

            var targetedFields = new TargetedFields
            {
                Attributes    = _targetedFields.Attributes.ToHashSet(),
                Relationships = _targetedFields.Relationships.ToHashSet()
            };

            return(new OperationContainer(kind, primaryResource, targetedFields, request));
        }
コード例 #8
0
        private ResourceObject GetRequiredSingleDataForResourceOperation(AtomicOperationObject operation)
        {
            if (operation.Data == null)
            {
                throw new JsonApiSerializationException("The 'data' element is required.", null, atomicOperationIndex: AtomicOperationIndex);
            }

            if (operation.SingleData == null)
            {
                throw new JsonApiSerializationException("Expected single data element for create/update resource operation.", null,
                                                        atomicOperationIndex: AtomicOperationIndex);
            }

            return(operation.SingleData);
        }
コード例 #9
0
        private void ParseDataForRelationship(RelationshipAttribute relationship,
                                              ResourceContext secondaryResourceContext,
                                              AtomicOperationObject operation, IIdentifiable primaryResource)
        {
            if (relationship is HasOneAttribute)
            {
                if (operation.ManyData != null)
                {
                    throw new JsonApiSerializationException(
                              "Expected single data element for to-one relationship.",
                              $"Expected single data element for '{relationship.PublicName}' relationship.",
                              atomicOperationIndex: AtomicOperationIndex);
                }

                if (operation.SingleData != null)
                {
                    ValidateSingleDataForRelationship(operation.SingleData, secondaryResourceContext, "data");

                    var secondaryResource = ParseResourceObject(operation.SingleData);
                    relationship.SetValue(primaryResource, secondaryResource);
                }
            }
            else if (relationship is HasManyAttribute)
            {
                if (operation.ManyData == null)
                {
                    throw new JsonApiSerializationException(
                              "Expected data[] element for to-many relationship.",
                              $"Expected data[] element for '{relationship.PublicName}' relationship.",
                              atomicOperationIndex: AtomicOperationIndex);
                }

                var secondaryResources = new List <IIdentifiable>();

                foreach (var resourceObject in operation.ManyData)
                {
                    ValidateSingleDataForRelationship(resourceObject, secondaryResourceContext, "data[]");

                    var secondaryResource = ParseResourceObject(resourceObject);
                    secondaryResources.Add(secondaryResource);
                }

                var rightResources =
                    TypeHelper.CopyToTypedCollection(secondaryResources, relationship.Property.PropertyType);
                relationship.SetValue(primaryResource, rightResources);
            }
        }