commit 47b018226fb8499bbfa5aca395a5ea48f9c13e57
parent ab67c8b07191a42ea50ce12f4876156662676442
Author: srfsh <dev@srf.sh>
Date: Mon, 29 Aug 2022 11:33:40 +0300
Zenflows.File: init
Diffstat:
4 files changed, 230 insertions(+), 0 deletions(-)
diff --git a/src/zenflows/file.ex b/src/zenflows/file.ex
@@ -0,0 +1,114 @@
+# Zenflows is designed to implement the Valueflows vocabulary,
+# written and maintained by srfsh <info@dyne.org>.
+# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+defmodule Zenflows.File do
+@moduledoc """
+File representation in storage.
+"""
+
+use Zenflows.DB.Schema
+
+require Logger
+
+alias Zenflows.VF.{
+ Agent,
+ EconomicResource,
+ Intent,
+ RecipeResource,
+ ResourceSpecification,
+ Validate,
+}
+
+@type t() :: %__MODULE__{
+ hash: String.t(),
+ name: String.t(),
+ description: String.t(),
+ mime_type: String.t(),
+ extension: String.t(),
+ size: pos_integer(),
+ signature: String.t(),
+ width: pos_integer() | nil,
+ height: pos_integer() | nil,
+ bin: String.t() | nil,
+ recipe_resource: RecipeResource.t() | nil,
+ economic_resource: EconomicResource.t() | nil,
+ agent: Agent.t() | nil,
+ resource_specification: ResourceSpecification.t() | nil,
+ intent: Intent.t() | nil,
+}
+
+schema "zf_file" do
+ field :hash, :string
+ field :name, :string
+ field :description, :string
+ field :mime_type, :string
+ field :extension, :string
+ field :size, :integer
+ field :signature, :string
+ field :width, :integer
+ field :height, :integer
+ field :bin, :string
+ timestamps()
+
+ belongs_to :recipe_resource, RecipeResource
+ belongs_to :economic_resource, EconomicResource
+ belongs_to :agent, Agent
+ belongs_to :resource_specification, ResourceSpecification
+ belongs_to :intent, Intent
+end
+
+@reqr ~w[hash name description mime_type extension size signature]a
+@cast @reqr ++ ~w[
+ width height bin
+ recipe_resource_id economic_resource_id agent_id
+ resource_specification_id intent_id
+]a
+
+@spec chgset(Schema.t(), params()) :: Changeset.t()
+def chgset(schema \\ %__MODULE__{}, params) do
+ schema
+ |> Changeset.cast(params, @cast)
+ |> Changeset.validate_required(@reqr)
+ |> Validate.key(:hash)
+ |> Validate.name(:name)
+ |> Validate.note(:description)
+ |> Validate.name(:mime_type)
+ |> Validate.name(:extension)
+ |> Changeset.validate_number(:size, greater_than: 0, less_than_or_equal_to: 1024 * 1024 * 25)
+ |> log_size_warning()
+ |> Validate.key(:signature)
+ |> Changeset.validate_number(:width, greater_than: 0)
+ |> Changeset.validate_number(:height, greater_than: 0)
+ |> Validate.img(:bin)
+ |> Changeset.assoc_constraint(:recipe_resource)
+ |> Changeset.assoc_constraint(:economic_resource)
+ |> Changeset.assoc_constraint(:agent)
+ |> Changeset.assoc_constraint(:resource_specification)
+ |> Changeset.assoc_constraint(:intent)
+ |> Changeset.unique_constraint(:hash)
+ |> Changeset.check_constraint(:general, name: :mutex, message: """
+ one of RecipeResource, EconomicResource, Agent, ResourceSpecification, or Intent must be provided.
+ """)
+end
+
+defp log_size_warning(cset) do
+ with {:ok, hash} <- Changeset.fetch_change(cset, :hash),
+ {:ok, n} when n > 1024 * 1024 * 4 <- Changeset.fetch_change(cset, :size),
+ do: Logger.warning("file exceeds 4MiB: #{inspect(hash)}")
+ cset
+end
+end
diff --git a/src/zenflows/file/domain.ex b/src/zenflows/file/domain.ex
@@ -0,0 +1,42 @@
+# Zenflows is designed to implement the Valueflows vocabulary,
+# written and maintained by srfsh <info@dyne.org>.
+# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+defmodule Zenflows.File.Domain do
+@moduledoc "Domain logic of Files."
+
+alias Zenflows.DB.{Paging, Repo}
+alias Zenflows.File
+
+@typep repo() :: Ecto.Repo.t()
+@typep id() :: Zenflows.DB.Schema.id()
+
+@spec one(repo(), id() | map() | Keyword.t())
+ :: {:ok, File.t()} | {:error, String.t()}
+def one(repo \\ Repo, _)
+def one(repo, id) when is_binary(id), do: one(repo, id: id)
+def one(repo, clauses) do
+ case repo.get_by(File, clauses) do
+ nil -> {:error, "not found"}
+ found -> {:ok, found}
+ end
+end
+
+@spec all(Paging.params()) :: Paging.result()
+def all(params) do
+ Paging.page(File, params)
+end
+end
diff --git a/src/zenflows/file/resolv.ex b/src/zenflows/file/resolv.ex
@@ -0,0 +1,24 @@
+# Zenflows is designed to implement the Valueflows vocabulary,
+# written and maintained by srfsh <info@dyne.org>.
+# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+defmodule Zenflows.File.Resolv do
+@moduledoc "Resolvers of Files."
+
+def date(%{id: id}, _, _) do
+ Zenflows.DB.ID.ts(id)
+end
+end
diff --git a/src/zenflows/file/type.ex b/src/zenflows/file/type.ex
@@ -0,0 +1,50 @@
+# Zenflows is designed to implement the Valueflows vocabulary,
+# written and maintained by srfsh <info@dyne.org>.
+# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+defmodule Zenflows.File.Type do
+@moduledoc "GraphQL types of Files."
+
+use Absinthe.Schema.Notation
+
+alias Zenflows.File.Resolv
+
+object :file do
+ field :hash, non_null(:url64)
+ field :name, non_null(:string)
+ field :description, non_null(:string)
+ field :date, non_null(:datetime), resolve: &Resolv.date/3
+ field :mime_type, non_null(:string)
+ field :extension, non_null(:string)
+ field :size, non_null(:integer)
+ field :signature, non_null(:string)
+ field :width, :integer
+ field :height, :integer
+ field :bin, :base64
+end
+
+input_object :ifile, name: "IFile" do
+ field :hash, non_null(:url64)
+ field :name, non_null(:string)
+ field :description, non_null(:string)
+ field :mime_type, non_null(:string)
+ field :extension, non_null(:string)
+ field :size, non_null(:integer)
+ field :signature, non_null(:string)
+ field :width, :integer
+ field :height, :integer
+end
+end