/// <summary>
        /// Get the representation.
        /// </summary>
        /// <exception cref="ArgumentNullException">Thrown when a parameter is null or empty</exception>
        /// <param name="identifier">Identifier of the representation.</param>
        /// <param name="locationPattern">Location pattern of the representation.</param>
        /// <param name="schemaId">Identifier of the schema.</param>
        /// <returns>Representation or null if it doesn't exist.</returns>
        public async Task <ApiActionResult> Execute(string identifier, string locationPattern, string schemaId)
        {
            // 1. Check parameters.
            if (string.IsNullOrWhiteSpace(identifier))
            {
                throw new ArgumentNullException(nameof(identifier));
            }

            _parametersValidator.ValidateLocationPattern(locationPattern);
            if (string.IsNullOrWhiteSpace(schemaId))
            {
                throw new ArgumentNullException(nameof(schemaId));
            }

            // 2. Check representation exists.
            var representation = await _representationStore.GetRepresentation(identifier);

            if (representation == null)
            {
                return(_apiResponseFactory.CreateError(
                           HttpStatusCode.NotFound,
                           string.Format(ErrorMessages.TheResourceDoesntExist, identifier)));
            }

            // 3. Parse the result and returns the representation.
            var result = await _responseParser.Parse(representation, locationPattern.Replace("{id}", representation.Id), schemaId, OperationTypes.Query);

            return(_apiResponseFactory.CreateResultWithContent(HttpStatusCode.OK, result.Object, result.Location, representation.Version, representation.Id));
        }
Example #2
0
        public async Task <ApiActionResult> Execute(JObject jObj, string locationPattern, string schemaId, string resourceType, string id)
        {
            if (jObj == null)
            {
                throw new ArgumentNullException(nameof(jObj));
            }

            _parametersValidator.ValidateLocationPattern(locationPattern);
            if (string.IsNullOrWhiteSpace(schemaId))
            {
                throw new ArgumentNullException(nameof(schemaId));
            }

            if (string.IsNullOrWhiteSpace(resourceType))
            {
                throw new ArgumentNullException(nameof(resourceType));
            }

            if (string.IsNullOrWhiteSpace(id))
            {
                throw new ArgumentNullException(nameof(id));
            }

            // 1. Check resource exists.
            if (await _representationStore.GetRepresentation(id) != null)
            {
                return(_apiResponseFactory.CreateError(
                           HttpStatusCode.InternalServerError,
                           string.Format(ErrorMessages.TheResourceAlreadyExist, id)));
            }

            // 2. Parse the request
            var result = await _requestParser.Parse(jObj, schemaId, CheckStrategies.Strong);

            if (!result.IsParsed)
            {
                return(_apiResponseFactory.CreateError(HttpStatusCode.InternalServerError,
                                                       result.ErrorMessage));
            }

            // 3. Set parameters
            result.Representation.Id           = id;
            result.Representation.Created      = DateTime.UtcNow;
            result.Representation.LastModified = DateTime.UtcNow;
            result.Representation.ResourceType = resourceType;
            result.Representation.Version      = Guid.NewGuid().ToString();

            // 4. Save the request
            await _representationStore.AddRepresentation(result.Representation);

            // 5. Transform and returns the representation.
            var response = await _responseParser.Parse(result.Representation, locationPattern.Replace("{id}", result.Representation.Id), schemaId, OperationTypes.Modification);

            return(_apiResponseFactory.CreateResultWithContent(HttpStatusCode.Created, response.Object, response.Location, result.Representation.Version, result.Representation.Id));
        }
        public async Task <ApiActionResult> Execute(string id, JObject jObj, string schemaId, string locationPattern)
        {
            // 1. Check parameters.
            if (string.IsNullOrWhiteSpace(id))
            {
                throw new ArgumentNullException(nameof(id));
            }

            if (jObj == null)
            {
                throw new ArgumentNullException(nameof(jObj));
            }

            if (string.IsNullOrWhiteSpace(schemaId))
            {
                throw new ArgumentNullException(nameof(schemaId));
            }

            _parametersValidator.ValidateLocationPattern(locationPattern);

            // 2. Check representation exists
            var representation = await _representationStore.GetRepresentation(id);

            if (representation == null)
            {
                return(_apiResponseFactory.CreateError(
                           HttpStatusCode.NotFound,
                           string.Format(ErrorMessages.TheResourceDoesntExist, id)));
            }

            // 3. Get patch operations.
            ErrorResponse errorResponse;
            var           operations = _patchRequestParser.Parse(jObj, out errorResponse);

            if (operations == null)
            {
                return(_apiResponseFactory.CreateError(
                           (HttpStatusCode)errorResponse.Status,
                           errorResponse));
            }

            // 4. Process operations.
            foreach (var operation in operations)
            {
                // 4.1 Check path is filled-in.
                if (operation.Type == PatchOperations.remove &&
                    string.IsNullOrWhiteSpace(operation.Path))
                {
                    return(_apiResponseFactory.CreateError(
                               HttpStatusCode.BadRequest,
                               _errorResponseFactory.CreateError(ErrorMessages.ThePathNeedsToBeSpecified, HttpStatusCode.BadRequest, Common.Constants.ScimTypeValues.InvalidSyntax)));
                }

                // 4.2 Check value is filled-in.
                if ((operation.Type == PatchOperations.add || operation.Type == PatchOperations.replace) &&
                    operation.Value == null)
                {
                    return(_apiResponseFactory.CreateError(
                               HttpStatusCode.BadRequest,
                               _errorResponseFactory.CreateError(ErrorMessages.TheValueNeedsToBeSpecified, HttpStatusCode.BadRequest, Common.Constants.ScimTypeValues.InvalidSyntax)));
                }

                // 4.3 Process filter & get values.
                IEnumerable <RepresentationAttribute> attrs         = null;
                IEnumerable <RepresentationAttribute> filteredAttrs = null;
                if (!string.IsNullOrWhiteSpace(operation.Path))
                {
                    // 4.3.1 Process filter.
                    var filter   = _filterParser.Parse(operation.Path);
                    var filtered = filter.Evaluate(representation);
                    if (filtered == null || !filtered.Any())
                    {
                        return(_apiResponseFactory.CreateError(
                                   HttpStatusCode.BadRequest,
                                   _errorResponseFactory.CreateError(ErrorMessages.TheFilterIsNotCorrect, HttpStatusCode.BadRequest, Common.Constants.ScimTypeValues.InvalidFilter)
                                   ));
                    }

                    // 4.3.2 Get targeted attributes.
                    var target = _filterParser.GetTarget(operation.Path);
                    var filterRepresentation = _filterParser.Parse(target);
                    attrs = filterRepresentation.Evaluate(representation);

                    if (operation.Type == PatchOperations.remove)
                    {
                        // 4.3.3 If operation = remove then values are not retrieved.
                        filteredAttrs = filtered;
                    }
                    else
                    {
                        // 4.3.4 if operation = replace or add then retrieve values.
                        var name  = filtered.First().SchemaAttribute.Name;
                        var token = operation.Value.SelectToken(name);
                        if (token == null)
                        {
                            token       = new JObject();
                            token[name] = operation.Value;
                        }

                        var value = _jsonParser.GetRepresentation(token, filtered.First().SchemaAttribute, CheckStrategies.Standard);
                        if (!value.IsParsed)
                        {
                            return(_apiResponseFactory.CreateError(
                                       HttpStatusCode.BadRequest,
                                       _errorResponseFactory.CreateError(value.ErrorMessage, HttpStatusCode.BadRequest, Common.Constants.ScimTypeValues.InvalidSyntax)));
                        }
                        filteredAttrs = new[] { value.RepresentationAttribute };
                    }
                }

                // 4.4 If there's no filter then parse the value with the schema.
                if (filteredAttrs == null)
                {
                    if (operation.Value != null)
                    {
                        var repr = await _representationRequestParser.Parse(operation.Value, schemaId, CheckStrategies.Standard);

                        if (!repr.IsParsed)
                        {
                            return(_apiResponseFactory.CreateError(
                                       HttpStatusCode.BadRequest,
                                       _errorResponseFactory.CreateError(repr.ErrorMessage, HttpStatusCode.BadRequest, Common.Constants.ScimTypeValues.InvalidSyntax)));
                        }

                        filteredAttrs = repr.Representation.Attributes;
                        attrs         = representation.Attributes;
                    }
                }

                foreach (var filteredAttr in filteredAttrs)
                {
                    var attr = attrs.FirstOrDefault(a => a.SchemaAttribute.Name == filteredAttr.SchemaAttribute.Name);
                    // 4.5.1 Check mutability.
                    if (filteredAttr.SchemaAttribute.Mutability == Common.Constants.SchemaAttributeMutability.Immutable ||
                        filteredAttr.SchemaAttribute.Mutability == Common.Constants.SchemaAttributeMutability.ReadOnly)
                    {
                        return(_apiResponseFactory.CreateError(
                                   HttpStatusCode.BadRequest,
                                   _errorResponseFactory.CreateError(string.Format(ErrorMessages.TheImmutableAttributeCannotBeUpdated, filteredAttr.SchemaAttribute.Name), HttpStatusCode.BadRequest, Common.Constants.ScimTypeValues.Mutability)));
                    }

                    // 4.5.2 Check uniqueness
                    if (filteredAttr.SchemaAttribute.Uniqueness == Common.Constants.SchemaAttributeUniqueness.Server) // TH  : SELECT THE VALUE AND CHECK THE UNIQUENESS.
                    {
                        var filter      = _filterParser.Parse(filteredAttr.FullPath);
                        var uniqueAttrs = await _representationStore.SearchValues(representation.ResourceType, filter);

                        if (uniqueAttrs.Any())
                        {
                            if (uniqueAttrs.Any(a => a.CompareTo(filteredAttr) == 0))
                            {
                                return(_apiResponseFactory.CreateError(
                                           HttpStatusCode.BadRequest,
                                           _errorResponseFactory.CreateError(string.Format(ErrorMessages.TheAttributeMustBeUnique, filteredAttr.SchemaAttribute.Name), HttpStatusCode.BadRequest, Common.Constants.ScimTypeValues.Uniqueness)));
                            }
                        }
                    }

                    switch (operation.Type)
                    {
                    // 4.5.3.1 Remove attributes.
                    case PatchOperations.remove:
                        if (attr == null)
                        {
                            return(_apiResponseFactory.CreateError(
                                       HttpStatusCode.BadRequest,
                                       _errorResponseFactory.CreateError(string.Format(ErrorMessages.TheAttributeDoesntExist, filteredAttr.SchemaAttribute.Name), HttpStatusCode.BadRequest)
                                       ));
                        }

                        if (filteredAttr.SchemaAttribute.MultiValued)
                        {
                            // 4.5.3.1.1 Remove attribute from array
                            if (!Remove(attr, filteredAttr))
                            {
                                return(_apiResponseFactory.CreateError(
                                           HttpStatusCode.BadRequest,
                                           _errorResponseFactory.CreateError(ErrorMessages.TheRepresentationCannotBeRemoved, HttpStatusCode.BadRequest)
                                           ));
                            }
                        }
                        else
                        {
                            // 4.5.3.1.2 Remove attribute from complex representation.
                            if (attr.Parent != null)
                            {
                                var complexParent = attr.Parent as ComplexRepresentationAttribute;
                                if (complexParent == null)
                                {
                                    continue;
                                }

                                complexParent.Values = complexParent.Values.Where(v => !v.Equals(attr));
                            }
                            else
                            {
                                representation.Attributes = representation.Attributes.Where(v => !v.Equals(attr));
                            }
                        }
                        break;

                    // 4.5.3.2 Add attribute.
                    case PatchOperations.add:
                        if (string.IsNullOrWhiteSpace(operation.Path) && attr == null)
                        {
                            representation.Attributes = representation.Attributes.Concat(new[] { filteredAttr });
                            continue;
                        }

                        if (attr == null)
                        {
                            return(_apiResponseFactory.CreateError(
                                       HttpStatusCode.BadRequest,
                                       _errorResponseFactory.CreateError(string.Format(ErrorMessages.TheAttributeDoesntExist, filteredAttr.SchemaAttribute.Name), HttpStatusCode.BadRequest)
                                       ));
                        }

                        if (!filteredAttr.SchemaAttribute.MultiValued)
                        {
                            if (!Set(attr, filteredAttr))
                            {
                                return(_apiResponseFactory.CreateError(
                                           HttpStatusCode.BadRequest,
                                           _errorResponseFactory.CreateError(ErrorMessages.TheRepresentationCannotBeSet, HttpStatusCode.BadRequest)
                                           ));
                            }
                        }
                        else
                        {
                            if (!Add(attr, filteredAttr))
                            {
                                return(_apiResponseFactory.CreateError(
                                           HttpStatusCode.BadRequest,
                                           _errorResponseFactory.CreateError(ErrorMessages.TheRepresentationCannotBeAdded, HttpStatusCode.BadRequest)
                                           ));
                            }
                        }
                        break;

                    // 4.5.3.3 Replace attribute
                    case PatchOperations.replace:
                        if (attr == null)
                        {
                            return(_apiResponseFactory.CreateError(
                                       HttpStatusCode.BadRequest,
                                       _errorResponseFactory.CreateError(string.Format(ErrorMessages.TheAttributeDoesntExist, filteredAttr.SchemaAttribute.Name), HttpStatusCode.BadRequest)
                                       ));
                        }

                        if (attr.SchemaAttribute.MultiValued)
                        {
                            if (!SetEnum(attr, filteredAttr))
                            {
                                return(_apiResponseFactory.CreateError(
                                           HttpStatusCode.BadRequest,
                                           _errorResponseFactory.CreateError(ErrorMessages.TheRepresentationCannotBeSet, HttpStatusCode.BadRequest)
                                           ));
                            }
                        }
                        else
                        {
                            if (!Set(attr, filteredAttr))
                            {
                                return(_apiResponseFactory.CreateError(
                                           HttpStatusCode.BadRequest,
                                           _errorResponseFactory.CreateError(ErrorMessages.TheRepresentationCannotBeSet, HttpStatusCode.BadRequest)
                                           ));
                            }
                        }
                        break;
                    }
                }
            }

            // 5. Save the representation.
            representation.Version = Guid.NewGuid().ToString();
            await _representationStore.UpdateRepresentation(representation);

            // 6. Returns the JSON representation.
            var response = await _responseParser.Parse(representation, locationPattern.Replace("{id}", id), schemaId, OperationTypes.Modification);

            return(_apiResponseFactory.CreateResultWithContent(HttpStatusCode.OK,
                                                               response.Object,
                                                               response.Location,
                                                               representation.Version,
                                                               representation.Id));
        }
        public async Task <ApiActionResult> Execute(string id, JObject jObj, string schemaId, string locationPattern, string resourceType)
        {
            // 1. Check parameters.
            if (string.IsNullOrWhiteSpace(id))
            {
                throw new ArgumentNullException(nameof(id));
            }

            if (jObj == null)
            {
                throw new ArgumentNullException(nameof(jObj));
            }

            if (string.IsNullOrWhiteSpace(schemaId))
            {
                throw new ArgumentNullException(nameof(schemaId));
            }

            _parametersValidator.ValidateLocationPattern(locationPattern);
            if (string.IsNullOrWhiteSpace(resourceType))
            {
                throw new ArgumentNullException(nameof(resourceType));
            }

            // 2. Parse the request.
            var representation = await _requestParser.Parse(jObj, schemaId, CheckStrategies.Strong);

            if (!representation.IsParsed)
            {
                return(_apiResponseFactory.CreateError(
                           HttpStatusCode.BadRequest,
                           representation.ErrorMessage));
            }

            var record = await _representationStore.GetRepresentation(id);

            // 3. If the representation doesn't exist then 404 is returned.
            if (record == null)
            {
                return(_apiResponseFactory.CreateError(
                           HttpStatusCode.NotFound,
                           string.Format(ErrorMessages.TheResourceDoesntExist, id)));
            }

            // 4. Update attributes.
            // var allRepresentations = (await _representationStore.GetRepresentations(record.ResourceType)).Where(r => r.Id != record.Id);
            var updateRepr = await UpdateRepresentation(record, representation.Representation);

            if (updateRepr.IsError)
            {
                return(_apiResponseFactory.CreateError(HttpStatusCode.BadRequest, updateRepr.ErrorResponse));
            }

            // 5. Store the new representation.
            record.LastModified = DateTime.UtcNow;
            record.Version      = Guid.NewGuid().ToString();
            if (!await _representationStore.UpdateRepresentation(record))
            {
                return(_apiResponseFactory.CreateError(HttpStatusCode.InternalServerError,
                                                       ErrorMessages.TheRepresentationCannotBeUpdated));
            }

            // 6. Parse the new representation.
            var response = await _responseParser.Parse(record, locationPattern.Replace("{id}", id), schemaId, OperationTypes.Modification);

            return(_apiResponseFactory.CreateResultWithContent(HttpStatusCode.OK,
                                                               response.Object,
                                                               response.Location,
                                                               record.Version,
                                                               record.Id));
        }
Example #5
0
        public async Task <ApiActionResult> Execute(JObject jObj, string baseUrl)
        {
            // 1. Check parameter.
            if (jObj == null)
            {
                throw new ArgumentNullException(nameof(jObj));
            }

            // 2. Parse the request.
            var bulk = await _bulkRequestParser.Parse(jObj, baseUrl);

            if (!bulk.IsParsed)
            {
                return(_apiResponseFactory.CreateError(HttpStatusCode.InternalServerError,
                                                       bulk.ErrorResponse));
            }


            // 3. Execute bulk operation.
            var numberOfErrors   = 0;
            var operationsResult = new JArray();

            foreach (var operation in bulk.BulkResult.Operations)
            {
                ApiActionResult operationResult = null;
                if (operation.Method == HttpMethod.Post)
                {
                    operationResult = await _addRepresentationAction.Execute(operation.Data, operation.LocationPattern, operation.SchemaId, operation.ResourceType);
                }
                else if (operation.Method == HttpMethod.Put)
                {
                    operationResult = await _updateRepresentationAction.Execute(operation.ResourceId, operation.Data, operation.SchemaId, operation.LocationPattern, operation.ResourceType);
                }
                else if (operation.Method == HttpMethod.Delete)
                {
                    operationResult = await _deleteRepresentationAction.Execute(operation.ResourceId);
                }
                else if (operation.Method.Method == "PATCH")
                {
                    operationResult = await _patchRepresentationAction.Execute(operation.ResourceId, operation.Data, operation.SchemaId, operation.LocationPattern);
                }

                // 3.2. If maximum number of errors has been reached then return an error.
                if (!operationResult.IsSucceed())
                {
                    numberOfErrors++;
                    if (bulk.BulkResult.FailOnErrors.HasValue && numberOfErrors > bulk.BulkResult.FailOnErrors)
                    {
                        return(_apiResponseFactory.CreateError(HttpStatusCode.InternalServerError,
                                                               _errorResponseFactory.CreateError(
                                                                   string.Format(ErrorMessages.TheMaximumNumberOfErrorHasBeenReached, bulk.BulkResult.FailOnErrors),
                                                                   HttpStatusCode.InternalServerError,
                                                                   Common.Constants.ScimTypeValues.TooMany)));
                    }
                }

                operationsResult.Add(CreateOperationResponse(operationResult, operation));
            }

            var response = CreateResponse(operationsResult);

            return(_apiResponseFactory.CreateResultWithContent(HttpStatusCode.OK, response));
        }