Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/ash_json_api/controllers/helpers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -924,7 +924,7 @@ defmodule AshJsonApi.Controllers.Helpers do
_ ->
Request.add_error(
request,
"id path parameter not present in get route: #{request.url}",
AshJsonApi.Error.InvalidPathParam.exception(parameter: "id", url: request.url),
:id_path_param
)
end
Expand Down
37 changes: 37 additions & 0 deletions lib/ash_json_api/error/conflicting_params.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# SPDX-FileCopyrightText: 2019 ash_json_api contributors <https://github.com/ash-project/ash_json_api/graphs.contributors>
#
# SPDX-License-Identifier: MIT

defmodule AshJsonApi.Error.ConflictingParams do
@moduledoc """
Returned when path parameters and query parameters have conflicting names
"""

use Splode.Error,
class: :invalid,
fields: [:conflicting_keys, :detail]

def message(error) do
error.detail
end

def exception(opts) do
opts
|> Keyword.put_new(:detail, "conflict path and query params")
|> Keyword.drop([:conflicting_keys])
|> super()
end

defimpl AshJsonApi.ToJsonApiError do
def to_json_api_error(error) do
%AshJsonApi.Error{
id: Ash.UUID.generate(),
status_code: 400,
code: "invalid_query",
title: "InvalidQuery",
detail: error.detail,
meta: %{}
}
end
end
end
5 changes: 5 additions & 0 deletions lib/ash_json_api/error/error.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ defmodule AshJsonApi.Error do
[error]
end

def to_json_api_errors(domain, resource, error, type) when is_binary(error) do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally speaking we should expect to be handling specific errors here that can implement a protocol. What are you doing that is ending up passing a string here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh if its something internal, then we should have those return InvalidQuery errors instead of strings. Could you make that change instead? 🙇

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, added a bunch of new errors and tests for those errors, plus a fallback for if anything ever hits the binary handler again (just in case)

unknown_error = AshJsonApi.Error.UnknownError.exception(message: error)
to_json_api_errors(domain, resource, unknown_error, type)
end

def to_json_api_errors(domain, resource, error, type) do
if AshJsonApi.ToJsonApiError.impl_for(error) do
error
Expand Down
47 changes: 47 additions & 0 deletions lib/ash_json_api/error/invalid_filter.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# SPDX-FileCopyrightText: 2019 ash_json_api contributors <https://github.com/ash-project/ash_json_api/graphs.contributors>
#
# SPDX-License-Identifier: MIT

defmodule AshJsonApi.Error.InvalidFilter do
@moduledoc """
Returned when a filter parameter is invalid or malformed
"""

use Splode.Error,
class: :invalid,
fields: [:filter, :detail, :source_parameter]

def message(error) do
error.detail
end

def exception(opts) do
filter = opts[:filter]

detail =
case filter do
nil -> "Invalid filter"
filter -> "Invalid filter: #{filter}"
end

opts
|> Keyword.put_new(:detail, detail)
|> Keyword.put_new(:source_parameter, "filter")
|> Keyword.drop([:filter])
|> super()
end

defimpl AshJsonApi.ToJsonApiError do
def to_json_api_error(error) do
%AshJsonApi.Error{
id: Ash.UUID.generate(),
status_code: 400,
code: "invalid_filter",
title: "InvalidFilter",
detail: error.detail,
source_parameter: error.source_parameter,
meta: %{}
}
end
end
end
48 changes: 48 additions & 0 deletions lib/ash_json_api/error/invalid_path_param.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# SPDX-FileCopyrightText: 2019 ash_json_api contributors <https://github.com/ash-project/ash_json_api/graphs.contributors>
#
# SPDX-License-Identifier: MIT

defmodule AshJsonApi.Error.InvalidPathParam do
@moduledoc """
Returned when a required path parameter is missing or invalid
"""

use Splode.Error,
class: :invalid,
fields: [:parameter, :url, :detail]

def message(error) do
error.detail
end

def exception(opts) do
parameter = opts[:parameter]
url = opts[:url]

detail =
case {parameter, url} do
{nil, nil} -> "Required path parameter not present"
{param, nil} -> "#{param} path parameter not present"
{nil, url} -> "Required path parameter not present in route: #{url}"
{param, url} -> "#{param} path parameter not present in route: #{url}"
end

opts
|> Keyword.put_new(:detail, detail)
|> Keyword.drop([:parameter, :url])
|> super()
end

defimpl AshJsonApi.ToJsonApiError do
def to_json_api_error(error) do
%AshJsonApi.Error{
id: Ash.UUID.generate(),
status_code: 400,
code: "invalid_path_param",
title: "InvalidPathParam",
detail: error.detail,
meta: %{}
}
end
end
end
49 changes: 49 additions & 0 deletions lib/ash_json_api/error/invalid_sort.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# SPDX-FileCopyrightText: 2019 ash_json_api contributors <https://github.com/ash-project/ash_json_api/graphs.contributors>
#
# SPDX-License-Identifier: MIT

defmodule AshJsonApi.Error.InvalidSort do
@moduledoc """
Returned when a sort parameter is invalid or malformed
"""

use Splode.Error,
class: :invalid,
fields: [:sort, :field, :detail, :source_parameter]

def message(error) do
error.detail
end

def exception(opts) do
sort = opts[:sort]
field = opts[:field]

detail =
cond do
field -> "Invalid sort field: #{field}"
sort -> "Invalid sort: #{sort}"
true -> "Invalid sort parameter"
end

opts
|> Keyword.put_new(:detail, detail)
|> Keyword.put_new(:source_parameter, "sort")
|> Keyword.drop([:sort, :field])
|> super()
end

defimpl AshJsonApi.ToJsonApiError do
def to_json_api_error(error) do
%AshJsonApi.Error{
id: Ash.UUID.generate(),
status_code: 400,
code: "invalid_sort",
title: "InvalidSort",
detail: error.detail,
source_parameter: error.source_parameter,
meta: %{}
}
end
end
end
36 changes: 36 additions & 0 deletions lib/ash_json_api/error/missing_schema.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# SPDX-FileCopyrightText: 2019 ash_json_api contributors <https://github.com/ash-project/ash_json_api/graphs.contributors>
#
# SPDX-License-Identifier: MIT

defmodule AshJsonApi.Error.MissingSchema do
@moduledoc """
Returned when a required schema is not found for validation
"""

use Splode.Error,
class: :invalid,
fields: [:detail]

def message(error) do
error.detail
end

def exception(opts) do
opts
|> Keyword.put_new(:detail, "No schema found for validation")
|> super()
end

defimpl AshJsonApi.ToJsonApiError do
def to_json_api_error(error) do
%AshJsonApi.Error{
id: Ash.UUID.generate(),
status_code: 400,
code: "missing_schema",
title: "MissingSchema",
detail: error.detail,
meta: %{}
}
end
end
end
39 changes: 39 additions & 0 deletions lib/ash_json_api/error/unknown_error.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# SPDX-FileCopyrightText: 2019 ash_json_api contributors <https://github.com/ash-project/ash_json_api/graphs.contributors>
#
# SPDX-License-Identifier: MIT

defmodule AshJsonApi.Error.UnknownError do
@moduledoc """
Returned when an unexpected error occurs that doesn't fit other categories
"""

use Splode.Error,
class: :unknown,
fields: [:detail]

def message(error) do
error.detail
end

def exception(opts) do
message = opts[:message] || "An unknown error occurred"

opts
|> Keyword.put_new(:detail, message)
|> Keyword.drop([:message])
|> super()
end

defimpl AshJsonApi.ToJsonApiError do
def to_json_api_error(error) do
%AshJsonApi.Error{
id: Ash.UUID.generate(),
status_code: 500,
code: "unknown_error",
title: "UnknownError",
detail: error.detail,
meta: %{}
}
end
end
end
46 changes: 36 additions & 10 deletions lib/ash_json_api/request.ex
Original file line number Diff line number Diff line change
Expand Up @@ -349,15 +349,21 @@ defmodule AshJsonApi.Request do
end

defp validate_params(%{query_params: query_params, path_params: path_params} = request) do
if Enum.any?(Map.keys(query_params), &Map.has_key?(path_params, &1)) do
add_error(request, "conflict path and query params", request.route.type)
conflicting_keys = Enum.filter(Map.keys(query_params), &Map.has_key?(path_params, &1))

if conflicting_keys != [] do
add_error(
request,
AshJsonApi.Error.ConflictingParams.exception(conflicting_keys: conflicting_keys),
request.route.type
)
else
request
end
end

defp validate_href_schema(%{schema: nil} = request) do
add_error(request, "no schema found", request.route.type)
add_error(request, AshJsonApi.Error.MissingSchema.exception([]), request.route.type)
end

defp validate_href_schema(
Expand Down Expand Up @@ -394,14 +400,22 @@ defmodule AshJsonApi.Request do

case public_related(resource, path) do
nil ->
add_error(request, "Invalid filter included", request.route.type)
add_error(
request,
AshJsonApi.Error.InvalidFilter.exception(filter: relationship_path),
request.route.type
)

related ->
if AshJsonApi.Resource.Info.derive_filter?(related) do
path = Enum.map(path, &String.to_existing_atom/1)
%{request | filter_included: Map.put(request.filter_included, path, filter_statement)}
else
add_error(request, "Invalid filter included", request.route.type)
add_error(
request,
AshJsonApi.Error.InvalidFilter.exception(filter: relationship_path),
request.route.type
)
end
end
end)
Expand All @@ -425,14 +439,22 @@ defmodule AshJsonApi.Request do

case public_related(resource, path) do
nil ->
add_error(request, "Invalid sort included", request.route.type)
add_error(
request,
AshJsonApi.Error.InvalidSort.exception(sort: relationship_path),
request.route.type
)

related ->
if AshJsonApi.Resource.Info.derive_sort?(related) do
path = Enum.map(path, &String.to_existing_atom/1)
%{request | sort_included: Map.put(request.sort_included, path, sort_included)}
else
add_error(request, "Invalid sort included", request.route.type)
add_error(
request,
AshJsonApi.Error.InvalidSort.exception(sort: relationship_path),
request.route.type
)
end
end
end)
Expand Down Expand Up @@ -765,7 +787,7 @@ defmodule AshJsonApi.Request do
defp parse_filter(%{query_params: %{"filter" => filter}} = request) do
if request.action.type == :read && request.route.derive_filter? &&
AshJsonApi.Resource.Info.derive_filter?(request.resource) do
add_error(request, "invalid filter", request.route.type)
add_error(request, AshJsonApi.Error.InvalidFilter.exception([]), request.route.type)
else
%{request | arguments: Map.put(request.arguments, :filter, filter)}
end
Expand Down Expand Up @@ -800,7 +822,11 @@ defmodule AshJsonApi.Request do
%{request | sort: [{calc.name, order} | request.sort]}

true ->
add_error(request, "invalid sort #{field}", request.route.type)
add_error(
request,
AshJsonApi.Error.InvalidSort.exception(field: field),
request.route.type
)
end
end)
end
Expand All @@ -812,7 +838,7 @@ defmodule AshJsonApi.Request do
defp parse_sort(%{query_params: %{"sort" => sort}} = request) do
if request.action.type == :read && request.route.derive_sort? &&
AshJsonApi.Resource.Info.derive_sort?(request.resource) do
add_error(request, "invalid sort string", request.route.type)
add_error(request, AshJsonApi.Error.InvalidSort.exception(sort: sort), request.route.type)
else
%{request | arguments: Map.put(request.arguments, :sort, sort)}
end
Expand Down
Loading