public async Task <ApiActionResult> Execute(string resourceType, SearchParameter searchParameter, string locationPattern) { // 1. Check parameters. if (string.IsNullOrWhiteSpace(resourceType)) { throw new ArgumentNullException(nameof(resourceType)); } if (searchParameter == null) { throw new ArgumentNullException(nameof(searchParameter)); } _parametersValidator.ValidateLocationPattern(locationPattern); // 2. Get representations & add the common attributes. var representations = await _representationStore.GetRepresentations(resourceType); foreach (var representation in representations) { var location = locationPattern.Replace("{id}", representation.Id); representation.Attributes = representation.Attributes.Concat(new[] { await _commonAttributesFactory.CreateMetaDataAttribute(representation, location), await _commonAttributesFactory.CreateId(representation) }); } // 3. Filter the representations. var result = _representationResponseParser.Filter(representations, searchParameter); // 4. Construct response. return(new ApiActionResult { Content = CreateResponse(result), StatusCode = 200 }); }
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); ErrorResponse error; if (!UpdateRepresentation(record, representation.Representation, allRepresentations, out error)) { return(_apiResponseFactory.CreateError(HttpStatusCode.BadRequest, error)); } // 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)); }
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. var allRepresentations = await _representationStore.GetRepresentations(representation.ResourceType); 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 && allRepresentations != null && allRepresentations.Any()) { var filter = _filterParser.Parse(filteredAttr.FullPath); var uniqueAttrs = new List <RepresentationAttribute>(); foreach (var records in allRepresentations.Select(r => filter.Evaluate(r))) { uniqueAttrs.AddRange(records); } 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)); }