type.ex (4173B)
1 # Zenflows is designed to implement the Valueflows vocabulary, 2 # written and maintained by srfsh <info@dyne.org>. 3 # Copyright (C) 2021-2023 Dyne.org foundation <foundation@dyne.org>. 4 # 5 # This program is free software: you can redistribute it and/or modify 6 # it under the terms of the GNU Affero General Public License as published by 7 # the Free Software Foundation, either version 3 of the License, or 8 # (at your option) any later version. 9 # 10 # This program is distributed in the hope that it will be useful, 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 # GNU Affero General Public License for more details. 14 # 15 # You should have received a copy of the GNU Affero General Public License 16 # along with this program. If not, see <https://www.gnu.org/licenses/>. 17 18 defmodule Zenflows.GQL.Type do 19 @moduledoc "Custom types or overrides for Absinthe." 20 21 use Absinthe.Schema.Notation 22 23 alias Absinthe.Blueprint.Input 24 alias Zenflows.DB.ID 25 26 @desc "A [Unified Resource Identity](https://w.wiki/4W2Y) as String." 27 scalar :uri, name: "URI" do 28 parse &uri_parse/1 29 serialize & &1 30 end 31 32 @desc "A base64-encoded (requires padding and ignores whitespace) as String." 33 scalar :base64, name: "Base64" do 34 parse &base64_parse/1 35 serialize & &1 36 end 37 38 @desc "A base64url-encoded (requires nonpadding and ignores whitespace) as String." 39 scalar :url64, name: "Url64" do 40 parse &url64_parse/1 41 serialize & &1 42 end 43 44 @desc "A JSON document encoded as string." 45 scalar :json, name: "JSON" do 46 parse &json_parse/1 47 serialize & &1 48 end 49 50 @desc "Cursors for pagination" 51 object :page_info do 52 @desc """ 53 Cursor pointing to the first of the results returned, to be 54 used with `before` query parameter if the backend supports 55 reverse pagination. 56 """ 57 field :start_cursor, :id 58 59 @desc """ 60 Cursor pointing to the last of the results returned, to be used 61 with `after` query parameter if the backend supports forward 62 pagination. 63 """ 64 field :end_cursor, :id 65 66 @desc """ 67 True if there are more results before `startCursor`. If unable 68 to be determined, implementations should return `true` to allow 69 for requerying. 70 """ 71 field :has_previous_page, non_null(:boolean) 72 73 @desc """ 74 True if there are more results after `endCursor`. If unable 75 to be determined, implementations should return `true` to allow 76 for requerying. 77 """ 78 field :has_next_page, non_null(:boolean) 79 80 @desc "The total result count, if it can be determined." 81 field :total_count, :integer 82 83 @desc """ 84 The number of items requested per page. Allows the storage 85 backend to indicate this when it is responsible for setting a 86 default and the client does not provide it. Note this may be 87 different to the number of items returned, if there is less than 88 1 page of results. 89 """ 90 field :page_limit, :integer 91 end 92 93 # TODO: Decide whether we really want these to be valid URIs or just 94 # Strings. 95 @spec uri_parse(Input.t()) :: {:ok, String.t() | nil} | :error 96 defp uri_parse(%Input.String{value: v}), do: {:ok, v} 97 defp uri_parse(%Input.Null{}), do: {:ok, nil} 98 defp uri_parse(_), do: :error 99 100 @spec base64_parse(Input.t()) :: {:ok, String.t() | nil} | :error 101 defp base64_parse(%Input.String{value: v}) do 102 case Base.decode64(v, ignore: :whitespace, padding: true) do 103 {:ok, _} -> {:ok, v} 104 :error -> :error 105 end 106 end 107 defp base64_parse(%Input.Null{}), do: {:ok, nil} 108 defp base64_parse(_), do: :error 109 110 @spec url64_parse(Input.t()) :: {:ok, String.t() | nil} | :error 111 defp url64_parse(%Input.String{value: v}) do 112 case Base.url_decode64(v, ignore: :whitespace, padding: false) do 113 {:ok, _} -> {:ok, v} 114 :error -> :error 115 end 116 end 117 defp url64_parse(%Input.Null{}), do: {:ok, nil} 118 defp url64_parse(_), do: :error 119 120 @spec json_parse(Input.t()) :: {:ok, String.t() | nil} | :error 121 defp json_parse(%Input.String{value: v}) do 122 case Jason.decode(v) do 123 {:ok, map} -> {:ok, map} 124 {:error, _} -> :error 125 end 126 end 127 defp json_parse(%Input.Null{}), do: {:ok, nil} 128 defp json_parse(_), do: :error 129 130 @spec id_parse(Input.t()) :: {:ok, ID.t() | nil} | :error 131 def id_parse(%Input.String{value: v}), do: ID.cast(v) 132 def id_parse(%Input.Null{}), do: {:ok, nil} 133 def id_parse(_), do: :error 134 end