lib/smart_city/dataset.ex

defmodule SmartCity.Dataset do
  @moduledoc """
  Struct defining a dataset definition and functions for retrieving key elements
  of the dataset for handling.

  ```javascript
  const Dataset = {
    "id": "",                // UUID
    "business": {            // Project Open Data Metadata Schema v1.1
      "authorEmail": "",
      "authorName": "",
      "benefitRating": 0,    // value between 0.0 and 1.0
      "categories": [""],
      "conformsToUri": "",
      "contactEmail": "",
      "contactName": "",
      "dataTitle": "",       // user friendly (dataTitle)
      "describedByMimeType": "",
      "describedByUrl": "",
      "description": "",
      "homepage": "",
      "issuedDate": "",
      "keywords": [""],
      "language": "",
      "license": "",
      "modifiedDate": "",
      "orgTitle": "",        // user friendly (orgTitle)
      "parentDataset": "",
      "publishFrequency": "",
      "referenceUrls": [""],
      "rights": "",
      "riskRating": 0,       // value between 0.0 and 1.0
      "spatial": "",
      "temporal": ""
    },
    "technical": {
      "allow_duplicates": true
      "authHeaders": {"header1": "", "header2": ""}
      "authBody": {"name": "", "clientID": ""}
      "authBodyEncodeMethod": "",
      "authUrl": "",
      "cadence": "",
      "dataName": "",        // ~r/[a-zA-Z_]+$/
      "orgId": "",
      "orgName": "",         // ~r/[a-zA-Z_]+$/
      "protocol": "",        // List of protocols to use. Defaults to nil. Can be [http1, http2]
      "schema": [{
        "name": "",
        "type": "",
        "description": ""
      }],
      "sourceHeaders": {
        "header1": "",
        "header2": ""
      },
      "sourceQueryParams": {
        "key1": "",
        "key2": ""
      },
      "sourceType": "",      // remote|stream|ingest|host
      "sourceUrl": "",
      "systemName": "",      // ${orgName}__${dataName}
      "topLevelSelector": ""
    }
  }
  ```
  """

  alias SmartCity.Dataset.Business
  alias SmartCity.Dataset.Technical

  @type id :: term()
  @type t :: %SmartCity.Dataset{
          business: SmartCity.Dataset.Business.t(),
          id: String.t(),
          technical: SmartCity.Dataset.Technical.t(),
          version: String.t()
        }

  @derive Jason.Encoder
  defstruct version: "0.6", id: nil, business: nil, technical: nil

  use Accessible

  alias SmartCity.BaseStruct

  @doc """
  Returns a new `SmartCity.Dataset` struct. `SmartCity.Dataset.Business` and
  `SmartCity.Dataset.Technical` structs will be created along the way.

  ## Parameters

  - msg : map defining values of the struct to be created.
    Can be initialized by
    - map with string keys
    - map with atom keys
    - JSON
  """

  @spec new(String.t() | map()) :: {:ok, map()} | {:error, term()}
  def new(msg) do
    msg
    |> BaseStruct.new()
    |> create()
  end

  defp create(%{id: id, business: biz, technical: tech}) do
    struct =
      struct(%__MODULE__{}, %{
        id: id,
        business: Business.new(biz),
        technical: Technical.new(tech)
      })

    {:ok, struct}
  rescue
    e -> {:error, e}
  end

  defp create(msg) do
    {:error, "Invalid Dataset: #{inspect(msg)}"}
  end

  @doc """
  Returns true if `SmartCity.Dataset.Technical sourceType field is stream`
  """
  def is_stream?(%__MODULE__{technical: %{sourceType: sourceType}}) do
    "stream" == sourceType
  end

  @doc """
  Returns true if `SmartCity.Dataset.Technical sourceType field is remote`
  """
  def is_remote?(%__MODULE__{technical: %{sourceType: sourceType}}) do
    "remote" == sourceType
  end

  @doc """
  Returns true if `SmartCity.Dataset.Technical sourceType field is ingest`
  """
  def is_ingest?(%__MODULE__{technical: %{sourceType: sourceType}}) do
    "ingest" == sourceType
  end

  @doc """
  Returns true if `SmartCity.Dataset.Technical sourceType field is host`
  """
  def is_host?(%__MODULE__{technical: %{sourceType: sourceType}}) do
    "host" == sourceType
  end
end

defimpl Brook.Deserializer.Protocol, for: SmartCity.Dataset do
  def deserialize(_struct, data) do
    SmartCity.Dataset.new(data)
  end
end