public bool TryTranslateMessage(Exception ex, out RESTError webServiceError)
        {
            webServiceError = null;

            var exception = ex is GenericADOException
                ? ex.InnerException
                : ex;

            if (exception is PostgresException postgresException)
            {
                var match = _expression.Match(postgresException.Message);

                if (match.Success)
                {
                    var exceptionInfo = new PostgresExceptionInfo(postgresException, _detailExpression);

                    var handledMessage = GetHandledMessage(exceptionInfo, match);

                    webServiceError = new RESTError
                    {
                        Code    = (int)HttpStatusCode.Conflict,
                        Type    = "Conflict",
                        Message = handledMessage
                    };

                    return(true);
                }
            }

            return(false);
        }
Beispiel #2
0
        public bool TryTranslateMessage(Exception ex, out RESTError webServiceError)
        {
            Preconditions.ThrowIfNull(ex, nameof(ex));

            webServiceError = null;

            // Unwrap the NHibernate generic exception if it is present
            var exception = ex is GenericADOException
                ? ex.InnerException
                : ex;

            if (exception is DatabaseConnectionException &&
                _snapshotContextProvider.GetSnapshotContext() != null)
            {
                webServiceError = new RESTError
                {
                    Code    = (int)HttpStatusCode.Gone,
                    Type    = HttpStatusCode.Gone.ToString().NormalizeCompositeTermForDisplay(),
                    Message = "Snapshot not available."
                };

                return(true);
            }

            return(false);
        }
Beispiel #3
0
        public bool TryTranslateMessage(Exception ex, out RESTError webServiceError)
        {
            webServiceError = null;

            var exception = ex is GenericADOException
                ? ex.InnerException
                : ex;

            if (exception is PostgresException postgresException)
            {
                var match = _expression.Match(postgresException.Message);

                if (match.Success)
                {
                    var exceptionInfo = new PostgresExceptionInfo(postgresException, _detailExpression);

                    string message = string.Format(exceptionInfo.IsComposedKeyConstraint
                        ? ComposedKeyMessageFormat
                        : SimpleKeyMessageFormat, exceptionInfo.Values, exceptionInfo.ColumnNames, exceptionInfo.TableName);

                    webServiceError = new RESTError
                    {
                        Code    = (int)HttpStatusCode.Conflict,
                        Type    = "Conflict",
                        Message = message
                    };

                    return(true);
                }
            }

            return(false);
        }
Beispiel #4
0
        public bool TryTranslateMessage(Exception ex, out RESTError webServiceError)
        {
            webServiceError = null;

            var exception = ex is GenericADOException
                ? ex.InnerException
                : ex;

            if (exception is SqlException)
            {
                var match = expression.Match(exception.Message);

                if (match.Success)
                {
                    string indexName = match.Groups["IndexName"]
                                       .Value;

                    string values = match.Groups["Values"]
                                    .Value;

                    if (string.IsNullOrWhiteSpace(values))
                    {
                        values = "unknown";
                    }

                    var indexDetails = _databaseMetadataProvider.GetIndexDetails(indexName);

                    string tableName = indexDetails == null
                        ? "unknown"
                        : indexDetails.TableName.ToCamelCase();

                    string columnNames = indexDetails == null
                        ? "unknown"
                        : string.Join("', '", indexDetails.ColumnNames.Select(x => x.ToCamelCase()));

                    string message;

                    if (indexDetails.ColumnNames.Count == 1)
                    {
                        message = string.Format(singleMessageFormat, values, columnNames, tableName);
                    }
                    else
                    {
                        message = string.Format(multipleMessageFormat, values, columnNames, tableName);
                    }

                    webServiceError = new RESTError
                    {
                        Code = (int)HttpStatusCode.Conflict, Type = "Conflict", Message = message
                    };

                    return(true);
                }
            }

            return(false);
        }
Beispiel #5
0
        public bool TryTranslateMessage(Exception ex, out RESTError webServiceError)
        {
            webServiceError = null;

            if (ex is EdFiSecurityException)
            {
                webServiceError = new RESTError
                {
                    Code = (int)HttpStatusCode.Forbidden, Type = "Forbidden", Message = ex.GetAllMessages()
                };

                return(true);
            }

            return(false);
        }
        public bool TryTranslateMessage(Exception ex, out RESTError webServiceError)
        {
            webServiceError = null;

            if (ex is NotModifiedException)
            {
                webServiceError = new RESTError
                {
                    Code = (int)HttpStatusCode.NotModified, Type = "Not Modified"
                };

                return(true);
            }

            return(false);
        }
        public bool TryTranslateMessage(Exception ex, out RESTError webServiceError)
        {
            webServiceError = null;

            if (ex is ConcurrencyException)
            {
                // See RFC 5789 - Conflicting modification (enforced internally, and no "If-Match" header)
                webServiceError = new RESTError
                {
                    Code = (int)HttpStatusCode.Conflict, Type = "Conflict", Message = ex.GetAllMessages()
                };

                return(true);
            }

            return(false);
        }
        public bool TryTranslateMessage(Exception ex, out RESTError webServiceError)
        {
            webServiceError = null;

            if (ex is StaleObjectStateException)
            {
                webServiceError = new RESTError
                {
                    Code = (int)HttpStatusCode.Conflict, Type = HttpStatusCode.Conflict.ToString(), Message =
                        "A natural key conflict occurred when attempting to update a new resource with a duplicate key. This is likely caused by multiple resources with the same key in the same file. Exactly one of these resources was updated."
                };

                return(true);
            }

            return(false);
        }
        public bool TryTranslateMessage(Exception ex, out RESTError webServiceError)
        {
            webServiceError = null;

            if (ex is NotFoundException)
            {
                webServiceError = new RESTError
                {
                    Code    = (int)HttpStatusCode.NotFound, Type = "Not Found",
                    Message = ex.GetAllMessages() ?? "The specified resource could not be found."
                };

                return(true);
            }

            return(false);
        }
Beispiel #10
0
        /// <summary>
        /// Attempts to translate the specified <see cref="Exception"/> to an error message that hides
        /// internal details of the service implementation and is palatable for consumers of the API.
        /// </summary>
        /// <param name="ex">The <see cref="Exception"/> to be translated.</param>
        /// <param name="webServiceError">The web service error response model.</param>
        /// <returns><b>true</b> if the exception was handled; otherwise <b>false</b>.</returns>
        public bool TryTranslateMessage(Exception ex, out RESTError webServiceError)
        {
            webServiceError = null;

            if (ex is ProfileContentTypeException)
            {
                webServiceError = new RESTError
                {
                    Code    = (int)HttpStatusCode.MethodNotAllowed, Type = HttpStatusCode.MethodNotAllowed.ToString(),
                    Message = ex.Message
                };

                return(true);
            }

            return(false);
        }
        public bool TryTranslateMessage(Exception ex, out RESTError webServiceError)
        {
            webServiceError = null;

            if (!_badRequestExceptionTypes.Contains(ex.GetType()))
            {
                return(false);
            }

            webServiceError = new RESTError
            {
                Code    = (int)HttpStatusCode.BadRequest,
                Type    = "Bad Request",
                Message = ex.GetAllMessages()
            };

            return(true);
        }
        public bool TryTranslateMessage(Exception ex, out RESTError webServiceError)
        {
            webServiceError = null;

            var exception = ex is GenericADOException
                ? ex.InnerException
                : ex;

            if (exception is SqlException)
            {
                var match = MatchPattern.Match(exception.Message);

                if (match.Success)
                {
                    string indexName = match.Groups["IndexName"]
                                       .Value;

                    string values = match.Groups["Values"]
                                    .Value;

                    var indexDetails = _databaseMetadataProvider.GetIndexDetails(indexName);

                    string tableName = indexDetails == null
                        ? "unknown"
                        : indexDetails.TableName;

                    string columnNames = indexDetails == null
                        ? "unknown"
                        : string.Join(", ", indexDetails.ColumnNames.Select(x => x));

                    var message = string.Format(MessageFormat, tableName, columnNames, values);

                    webServiceError = new RESTError
                    {
                        Code = (int)HttpStatusCode.Conflict, Type = HttpStatusCode.Conflict.ToString(), Message = message
                    };

                    return(true);
                }
            }

            return(false);
        }
Beispiel #13
0
        public bool TryTranslateMessage(Exception ex, out RESTError webServiceError)
        {
            Preconditions.ThrowIfNull(ex, nameof(ex));

            webServiceError = null;

            if (!ExceptionTypes.Contains(ex.GetType()))
            {
                return(false);
            }

            webServiceError = new RESTError
            {
                Code    = (int)ResponseCode,
                Type    = ResponseCode.ToString().NormalizeCompositeTermForDisplay(),
                Message = ex.GetAllMessages()
            };

            return(true);
        }
        public bool TryTranslateMessage(Exception ex, out RESTError webServiceError)
        {
            webServiceError = null;

            if (ex is NonUniqueObjectException)
            {
                var match = Regex.Match(ex.Message, ExpectedExceptionPattern);

                if (match.Success)
                {
                    try
                    {
                        webServiceError = new RESTError
                        {
                            Code = (int)HttpStatusCode.Conflict, Type = HttpStatusCode.Conflict.ToString(), Message =
                                string.Format(
                                    "A duplicate {0} conflict occurred when attempting to create a new {1} resource with {2} of {3}.",
                                    match.Groups["subject"]
                                    .Value,
                                    match.Groups["entitySimple"]
                                    .Value,
                                    match.Groups["property"]
                                    .Value,
                                    match.Groups["entityPropertyId"]
                                    .Value)
                        };
                    }
                    catch
                    {
                        return(false);
                    }

                    return(true);
                }
            }

            return(false);
        }
        public virtual IActionResult Get()
        {
            if (!_isEnabled)
            {
                return(NotFound());
            }

            object    json      = null;
            RESTError restError = null;

            try
            {
                var routeDataValues = Request.RouteValues;

                string organizationCode        = (string)routeDataValues["organizationCode"];
                string compositeCategory       = (string)routeDataValues["compositeCategory"];
                string compositeCollectionName = (string)routeDataValues["compositeName"];
                string compositeResourceName   = CompositeTermInflector.MakeSingular(compositeCollectionName);

                // Try to find the composite definition that matches the incoming URIs composite category/name
                if (!_compositeMetadataProvider.TryGetCompositeDefinition(
                        organizationCode,
                        compositeCategory,
                        compositeResourceName,
                        out XElement compositeDefinition))
                {
                    return(NotFound());
                }

                // Prepare query string parameters
                var rawQueryStringParameters = ParseQuery(Request.QueryString.ToString());

                var queryStringParameters =
                    rawQueryStringParameters.Keys.ToDictionary <string, string, object>(

                        // Replace underscores with periods for appropriate processing
                        kvp => kvp.Replace('_', '.'),
                        kvp => rawQueryStringParameters[kvp],
                        StringComparer.InvariantCultureIgnoreCase);

                //respond quickly to DOS style requests (should we catch these earlier?  e.g. attribute filter?)

                if (queryStringParameters.TryGetValue("limit", out object limitAsObject))
                {
                    if (int.TryParse(limitAsObject.ToString(), out int limit) &&
                        (limit <= 0 || limit > 100))
                    {
                        return(BadRequest(ErrorTranslator.GetErrorMessage("Limit must be omitted or set to a value between 1 and 100.")));
                    }
                }

                // Process specification for route and query string parameters
                var specificationParameters = GetCompositeSpecificationParameters();

                // Ensure all matched route key values were used by the current composite
                var suppliedSpecificationParameters =
                    routeDataValues
                    .Where(kvp => !_standardApiRouteKeys.Contains(kvp.Key))
                    .ToList();

                var unusedSpecificationParameters =
                    suppliedSpecificationParameters
                    .Where(kvp => !specificationParameters.ContainsKey(kvp.Key))
                    .ToList();

                if (unusedSpecificationParameters.Any())
                {
                    return(NotFound());
                }

                AddInherentSupportForIdParameter();

                json = _compositeResourceResponseProvider.Get(
                    compositeDefinition,
                    specificationParameters,
                    queryStringParameters,
                    GetNullValueHandling());

                void AddInherentSupportForIdParameter()
                {
                    if (!Request.RouteValues.TryGetValue("id", out object idAsObject))
                    {
                        return;
                    }

                    if (!Guid.TryParse(idAsObject.ToString(), out Guid id))
                    {
                        throw new BadRequestException("The supplied resource identifier is invalid.");
                    }

                    specificationParameters.Add(
                        "Id",
                        new CompositeSpecificationParameter
                    {
                        FilterPath = "Id",
                        Value      = id
                    });
                }

                IDictionary <string, CompositeSpecificationParameter> GetCompositeSpecificationParameters()
                {
                    var specificationElt = compositeDefinition.Element("Specification");

                    if (specificationElt == null)
                    {
                        return(new Dictionary <string, CompositeSpecificationParameter>());
                    }

                    var specificationFilterByName = specificationElt
                                                    .Elements("Parameter")
                                                    .ToDictionary(
                        p => p.AttributeValue("name"),
                        p => new
                    {
                        FilterPath = p.AttributeValue("filterPath"),
                        Queryable  = p.AttributeValue("queryable") == "true"
                    },
                        StringComparer.InvariantCultureIgnoreCase);

                    // Identify relevant route values
                    var matchingRouteValues = routeDataValues
                                              .Where(x => specificationFilterByName.ContainsKey(x.Key));

                    // Copy route values that match the specification to the parameter dictionary
                    var parameters = matchingRouteValues.ToDictionary(
                        kvp => kvp.Key, kvp => new CompositeSpecificationParameter
                    {
                        FilterPath = specificationFilterByName[kvp.Key]
                                     .FilterPath,
                        Value = kvp.Value
                    });

                    // Identify relevant query string values
                    var matchingQueryStringParameters = queryStringParameters

                                                        // Skip query string parameter matching if the key was already matched by the route
                                                        .Where(x => !parameters.ContainsKey(x.Key))
                                                        .Where(
                        x => specificationFilterByName.ContainsKey(x.Key) &&
                        specificationFilterByName[x.Key]
                        .Queryable)
                                                        .ToList();

                    // Copy route values that match the specification to the parameter dictionary
                    foreach (var kvp in matchingQueryStringParameters)
                    {
                        // Guids aren't "coerced" by SqlParameter correctly
                        object value = Guid.TryParse(kvp.Value as string, out Guid guidValue)
                            ? guidValue
                            : kvp.Value;

                        parameters.Add(
                            kvp.Key,
                            new CompositeSpecificationParameter
                        {
                            FilterPath = specificationFilterByName[kvp.Key]
                                         .FilterPath,
                            Value = value
                        });

                        // Remove the processed Specification-based query string parameter
                        queryStringParameters.Remove(kvp.Key);
                    }

                    return(parameters);
                }
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
                restError = _restErrorProvider.GetRestErrorFromException(ex);
            }

            if (restError != null)
            {
                return(string.IsNullOrWhiteSpace(restError.Message)
                    ? (IActionResult)StatusCode(restError.Code)
                    : StatusCode(restError.Code, restError.Message));
            }

            return(Ok(json));

            NullValueHandling GetNullValueHandling()
            {
                // The custom 'IncludeNulls' header is supported for testing purposes.
                if (Request.Headers.TryGetValue("IncludeNulls", out StringValues headerValues) &&
                    headerValues.Contains("true"))
                {
                    return(NullValueHandling.Include);
                }

                return(NullValueHandling.Ignore);
            }
        }
        public virtual IHttpActionResult Get()
        {
            string    json      = null;
            RESTError restError = null;

            try
            {
                var routeDataValues = ActionContext.RequestContext.RouteData.Values;

                string organizationCode        = (string)routeDataValues["organizationCode"];
                string compositeCategory       = (string)routeDataValues["compositeCategory"];
                string compositeCollectionName = (string)routeDataValues["compositeName"];
                string compositeResourceName   = CompositeTermInflector.MakeSingular(compositeCollectionName);

                // Try to find the composite definition that matches the incoming URIs composite category/name
                if (!_compositeMetadataProvider.TryGetCompositeDefinition(
                        organizationCode,
                        compositeCategory,
                        compositeResourceName,
                        out XElement compositeDefinition))
                {
                    return(NotFound());
                }

                // Prepare query string parameters
                var rawQueryStringParameters = ActionContext.Request.RequestUri.ParseQueryString();

                var queryStringParameters =
                    rawQueryStringParameters.AllKeys.ToDictionary <string, string, object>(

                        // Replace underscores with periods for appropriate processing
                        kvp => kvp.Replace('_', '.'),
                        kvp => rawQueryStringParameters[kvp],
                        StringComparer.InvariantCultureIgnoreCase);

                var defaultPageSizeLimit = _defaultPageSizeLimitProvider.GetDefaultPageSizeLimit();

                //respond quickly to DOS style requests (should we catch these earlier?  e.g. attribute filter?)
                if (queryStringParameters.TryGetValue("limit", out object limitAsObject))
                {
                    if (int.TryParse(limitAsObject.ToString(), out int limit) &&
                        (limit <= 0 || limit > defaultPageSizeLimit))
                    {
                        return(BadRequest($"Limit must be omitted or set to a value between 1 and max value defined in configuration file (defaultPageSizeLimit)."));
                    }
                }

                // Process specification for route and query string parameters
                var specificationParameters = GetCompositeSpecificationParameters(
                    compositeDefinition, routeDataValues, queryStringParameters);

                // Ensure all matched route key values were used by the current composite
                var suppliedSpecificationParameters =
                    routeDataValues
                    .Where(kvp => !StandardApiRouteKeys.Contains(kvp.Key));

                var unusedSpecificationParameters =
                    suppliedSpecificationParameters
                    .Where(kvp => !specificationParameters.ContainsKey(kvp.Key));

                if (unusedSpecificationParameters.Any())
                {
                    return(new StatusCodeResult(HttpStatusCode.NotFound, this));
                }

                AddInherentSupportForIdParameter(specificationParameters);

                json = _compositeResourceResponseProvider.GetJson(
                    compositeDefinition,
                    specificationParameters,
                    queryStringParameters,
                    GetNullValueHandling());
            }
            catch (Exception ex)
            {
                _logger.Error(ex);
                restError = _restErrorProvider.GetRestErrorFromException(ex);
            }

            if (restError != null)
            {
                return(string.IsNullOrWhiteSpace(restError.Message)
                    ? new StatusCodeResult((HttpStatusCode)restError.Code, this)
                    : new StatusCodeResult((HttpStatusCode)restError.Code, this).WithError(restError.Message));
            }

            return(new ResponseMessageResult(
                       new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent(json, Encoding.UTF8, "application/json")
            }));
        }
        // ^The (?<Statement>INSERT|UPDATE|DELETE) statement conflicted with the (?<ConstraintType>FOREIGN KEY|REFERENCE) constraint "(?<ConstraintName>\w+)".*?table "[a-z]+\.(?<TableName>\w+)".*?(?: column '(?<ColumnName>\w+)')?
        public bool TryTranslateMessage(Exception ex, out RESTError webServiceError)
        {
            webServiceError = null;

            var exception = ex is GenericADOException
                ? ex.InnerException
                : ex;

            if (exception is SqlException)
            {
                // Is this a constraint violation message from SQL Server?
                var match = _expression.Match(exception.Message);

                if (match.Success)
                {
                    string messageFormat = string.Empty;

                    string statementType = match.Groups["StatementType"]
                                           .Value;

                    string constraintType = match.Groups["ConstraintType"]
                                            .Value;

                    string tableName = match.Groups["TableName"]
                                       .Value;

                    string columnName = match.Groups["ColumnName"]
                                        .Value;

                    switch (statementType)
                    {
                    case "INSERT":
                    case "UPDATE":

                        if (constraintType == "FOREIGN KEY")
                        {
                            messageFormat = "The value supplied for the related '{0}' resource does not exist.";
                            break;
                        }

                        // No explicit support for UPDATE/REFERENCE constraint yet
                        return(false);

                    case "DELETE":

                        if (constraintType == "REFERENCE")
                        {
                            if (string.IsNullOrEmpty(columnName))
                            {
                                messageFormat =
                                    "The resource (or a subordinate entity of the resource) cannot be deleted because it is a dependency of the '{0}' entity.";
                            }
                            else
                            {
                                messageFormat =
                                    "The resource (or a subordinate entity of the resource) cannot be deleted because it is a dependency of the '{1}' value of the '{0}' entity.";
                            }

                            break;
                        }

                        // No explicit support for UPDATE/REFERENCE constraint yet
                        return(false);
                    }

                    string message = string.Format(messageFormat, tableName.ToCamelCase(), columnName.ToCamelCase());

                    webServiceError = new RESTError
                    {
                        Code = (int)HttpStatusCode.Conflict, Type = "Conflict", Message = message
                    };

                    return(true);
                }
            }

            return(false);
        }