From 31661cd0d09ee94c90fbe9d025676b787fb035cf Mon Sep 17 00:00:00 2001 From: javierg Date: Tue, 10 Jun 2025 11:53:45 -0700 Subject: [PATCH 1/2] Add credo for static analysis check and formatter file --- .formatter.exs | 3 + .gitignore | 2 +- lib/couchx/adapter.ex | 259 +++++++++++++---------- lib/couchx/constraint.ex | 90 ++++---- lib/couchx/couch_id.ex | 4 + lib/couchx/db_connection.ex | 88 ++++---- lib/couchx/document_state.ex | 19 +- lib/couchx/dynamic_repo.ex | 13 +- lib/couchx/finders.ex | 4 + lib/couchx/mango_index.ex | 7 +- lib/couchx/migrator.ex | 9 +- lib/couchx/prepare_query.ex | 31 ++- lib/couchx/query_handler.ex | 16 +- lib/couchx/support/application_helper.ex | 8 +- lib/mix/tasks/couchx.gen.mango_index.ex | 21 +- lib/mix/tasks/couchx.mango_index.down.ex | 6 +- lib/mix/tasks/couchx.mango_index.ex | 19 +- mix.exs | 17 +- mix.lock | 3 + 19 files changed, 369 insertions(+), 250 deletions(-) create mode 100644 .formatter.exs diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..d304ff3 --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,3 @@ +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/.gitignore b/.gitignore index 0eb82ec..0a9287a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ .tool-versions .idea - +/*.dump diff --git a/lib/couchx/adapter.ex b/lib/couchx/adapter.ex index e511f8a..2e00fa5 100644 --- a/lib/couchx/adapter.ex +++ b/lib/couchx/adapter.ex @@ -141,17 +141,17 @@ defmodule Couchx.Adapter do @impl true def init(config) do - config = put_conn_id(config) - log = Keyword.get(config, :log, :debug) + config = put_conn_id(config) + log = Keyword.get(config, :log, :debug) telemetry_prefix = Keyword.fetch!(config, :telemetry_prefix) - telemetry = {config[:repo], log, telemetry_prefix ++ [:query]} - spec = couchdb_supervisor_spec(config) + telemetry = {config[:repo], log, telemetry_prefix ++ [:query]} + spec = couchdb_supervisor_spec(config) {:ok, spec, %{telemetry: telemetry, opts: [returning: true], config: config}} end @impl true - def ensure_all_started(_repo, _type), do: HTTPoison.start + def ensure_all_started(_repo, _type), do: HTTPoison.start() @impl true def checkout(_adapter, _config, result), do: result @@ -168,8 +168,9 @@ defmodule Couchx.Adapter do @impl true def autogenerate(:id), do: nil + def autogenerate(:binary_id) do - Ecto.UUID.cast!(Ecto.UUID.bingenerate) + Ecto.UUID.cast!(Ecto.UUID.bingenerate()) end @impl true @@ -208,12 +209,12 @@ defmodule Couchx.Adapter do end def parse_bulk_response(%{"rev" => rev, "id" => doc_id}, data, schema) do - fillers = Enum.map(schema, fn(_)-> nil end) + fillers = Enum.map(schema, fn _ -> nil end) doc_template = Enum.zip(schema, fillers) - response_data = Enum.find(data, fn([{:_id, id} | _]) -> id == doc_id end) ++ [_rev: rev] + response_data = Enum.find(data, fn [{:_id, id} | _] -> id == doc_id end) ++ [_rev: rev] doc = Keyword.merge(doc_template, response_data) - Enum.map(schema, fn(key)-> Keyword.get(doc, key) end) + Enum.map(schema, fn key -> Keyword.get(doc, key) end) end def execute(:view, meta, design, view, key, query_opts) do @@ -223,12 +224,14 @@ defmodule Couchx.Adapter do def execute(:view, meta, design, view, query_opts) do opts = prepare_view_options(query_opts) + Couchx.DbConnection.get(meta[:pid], "_design/#{design}/_view/#{view}", opts) |> parse_view_response(opts[:include_docs], query_opts[:module]) end def execute(:find, meta, selector, fields, opts) do query = %{selector: selector, fields: fields} + Couchx.DbConnection.find(meta[:pid], query, opts) |> parse_view_response(opts[:include_docs], opts[:module]) end @@ -240,34 +243,39 @@ defmodule Couchx.Adapter do @impl true def execute(meta, query_meta, query_cache, params, _opts) do - {_, {_, query}} = query_cache - %{select: select} = query_meta - keys = fetch_query_keys(query_cache) - query_options = query[:options] || %{} - {all_fields, module} = fetch_fields(query_meta.sources) - namespace = build_namespace(module) + {_, {_, query}} = query_cache + %{select: select} = query_meta + keys = fetch_query_keys(query_cache) + query_options = query[:options] || %{} + {all_fields, module} = fetch_fields(query_meta.sources) + namespace = build_namespace(module) fields_meta = fields_meta(select[:from]) - fields = case select[:postprocess] do - {:map, keyfields} -> - Keyword.keys(keyfields) - |> Enum.map(&Atom.to_string/1) - _-> all_fields - end + fields = + case select[:postprocess] do + {:map, keyfields} -> + Keyword.keys(keyfields) + |> Enum.map(&Atom.to_string/1) - query = if select[:take] do - %{fields: select[:take]} - else - %{} - end + _ -> + all_fields + end + + query = + if select[:take] do + %{fields: select[:take]} + else + %{} + end do_query(meta[:pid], keys, namespace, params, Map.merge(query, query_options)) |> QueryHandler.query_results(fields, fields_meta) end defp fetch_query_keys({_, {_, query}}) - when query == [:delete], do: [:delete] + when query == [:delete], + do: [:delete] defp fetch_query_keys({_, {_, query}}), do: query[:keys] @@ -288,31 +296,34 @@ defmodule Couchx.Adapter do end defp fetch_fields({{resource, nil, _}}) do - module = ["Elixir", ".", resource] - |> Enum.map(&Inflex.singularize/1) - |> Enum.map(&String.capitalize/1) - |> Enum.join - |> String.to_existing_atom + module = + ["Elixir", ".", resource] + |> Enum.map(&Inflex.singularize/1) + |> Enum.map_join(&String.capitalize/1) + |> String.to_existing_atom() fetch_fields({{resource, module, nil}}) end defp fetch_fields({{_resource, module, _}}) do - fields = module.__struct__() - |> Map.keys - |> Kernel.--([:__struct__, :__meta__]) - |> Enum.map(&Atom.to_string/1) + fields = + module.__struct__() + |> Map.keys() + |> Kernel.--([:__struct__, :__meta__]) + |> Enum.map(&Atom.to_string/1) {fields, module} end - defp do_query(server, [%{_id: {:^, [], [0, _total]}}], namespace, ids, select) when is_list(ids) do + defp do_query(server, [%{_id: {:^, [], [0, _total]}}], namespace, ids, select) + when is_list(ids) do do_query(server, [%{_id: ids}], namespace, [], select) end defp do_query(server, [%{_id: ids}], namespace, [], _select) when is_list(ids) do - doc_ids = Enum.map(ids, &namespace_id(namespace, &1)) - |> Enum.map(&URI.decode_www_form/1) + doc_ids = + Enum.map(ids, &namespace_id(namespace, &1)) + |> Enum.map(&URI.decode_www_form/1) Couchx.DbConnection.all_docs(server, doc_ids, include_docs: true) |> sanitize_collection @@ -342,16 +353,27 @@ defmodule Couchx.Adapter do end defp do_query(server, [:delete], namespace, [], _select) do - {:ok, %{"rows" => rows}} = Couchx.DbConnection.get(server, "_all_docs", [limit: 100, include_docs: true, startkey: Jason.encode!(namespace), endkey: Jason.encode!("#{namespace}/{}")]) - docs = Enum.map(rows, fn(%{"doc" => doc})-> %{_id: doc["_id"], _rev: doc["_rev"], _deleted: true} end) + {:ok, %{"rows" => rows}} = + Couchx.DbConnection.get(server, "_all_docs", + limit: 100, + include_docs: true, + startkey: Jason.encode!(namespace), + endkey: Jason.encode!("#{namespace}/{}") + ) + + docs = + Enum.map(rows, fn %{"doc" => doc} -> + %{_id: doc["_id"], _rev: doc["_rev"], _deleted: true} + end) + Couchx.DbConnection.bulk_docs(server, docs) end defp do_query(server, [], namespace, [], query_options) do limit = query_options[:limit] || 100 - orders= query_options[:sort] + orders = query_options[:sort] - opts = [ + opts = [ include_docs: true, limit: limit, include_docs: true, @@ -359,26 +381,28 @@ defmodule Couchx.Adapter do endkey: Jason.encode!("#{namespace}/{}") ] - descending = if orders do - [default_order | _] = orders + descending = + if orders do + [default_order | _] = orders - default_order - |> Map.values - |> List.flatten - |> List.first - |> Kernel.==(:desc) - end + default_order + |> Map.values() + |> List.flatten() + |> List.first() + |> Kernel.==(:desc) + end - opts = if descending do - startkey = opts[:startkey] - endkey = opts[:endkey] + opts = + if descending do + startkey = opts[:startkey] + endkey = opts[:endkey] - Keyword.replace(opts, :startkey, endkey) - |> Keyword.replace(:endkey, startkey) - |> Kernel.++([descending: true]) - else - opts - end + Keyword.replace(opts, :startkey, endkey) + |> Keyword.replace(:endkey, startkey) + |> Kernel.++(descending: true) + else + opts + end {:ok, %{"rows" => rows}} = Couchx.DbConnection.get(server, "_all_docs", opts) Enum.map(rows, &Map.get(&1, "doc")) @@ -410,22 +434,24 @@ defmodule Couchx.Adapter do end defp process_property({key, selector}, acc, values) - when is_map(selector) do - with [operator] <- Map.keys(selector), - false <- operator == "$eq" do - %{key => process_selector(selector, values)} - else - true -> - case selector do - %{"$eq" => {_, [], [value_index]}} -> - value = Enum.fetch!(values, value_index) - Map.put(acc, key, value) - %{"$eq" => value} -> - Map.put(acc, key, value) - _ -> + when is_map(selector) do + with [operator] <- Map.keys(selector), + false <- operator == "$eq" do + %{key => process_selector(selector, values)} + else + true -> + case selector do + %{"$eq" => {_, [], [value_index]}} -> + value = Enum.fetch!(values, value_index) + Map.put(acc, key, value) + + %{"$eq" => value} -> + Map.put(acc, key, value) + + _ -> {:error, "unsupported selector"} - end - end + end + end end defp process_property({key, {:^, [], [value_index]}}, acc, values) do @@ -437,6 +463,7 @@ defmodule Couchx.Adapter do case key do "$or" -> Map.put(acc, "$and", [%{key => value}]) + _ -> Map.put(acc, key, value) end @@ -455,10 +482,10 @@ defmodule Couchx.Adapter do defp build_namespace(module) do module - |> to_string - |> String.split(".") - |> List.last - |> Macro.underscore + |> to_string + |> String.split(".") + |> List.last() + |> Macro.underscore() end defp namespace_id(namespace, id) do @@ -466,8 +493,8 @@ defmodule Couchx.Adapter do URI.encode_www_form(id) else namespace - |> Kernel.<>("/#{id}") - |> URI.encode_www_form + |> Kernel.<>("/#{id}") + |> URI.encode_www_form() end end @@ -477,12 +504,12 @@ defmodule Couchx.Adapter do defp build_id(data, %{schema: resource}) do resource - |> to_string - |> String.split(".") - |> List.last - |> Macro.underscore - |> Kernel.<>("/#{base_id(data._id)}") - |> update_data_id(data) + |> to_string + |> String.split(".") + |> List.last() + |> Macro.underscore() + |> Kernel.<>("/#{base_id(data._id)}") + |> update_data_id(data) end defp update_data_id(id, data) do @@ -509,9 +536,10 @@ defmodule Couchx.Adapter do end defp fetch_module_name(map, nil) do - doc_type = Map.get(map, "_id") - |> String.replace(~r{(/.+)}, "") - |> Macro.camelize + doc_type = + Map.get(map, "_id") + |> String.replace(~r{(/.+)}, "") + |> Macro.camelize() :"Elixir.SDB.#{doc_type}" end @@ -533,7 +561,7 @@ defmodule Couchx.Adapter do Couchx.DbConnection, :start_link, [ - config, + config ] }, restart: :permanent, @@ -548,10 +576,11 @@ defmodule Couchx.Adapter do # Pending implementation def delete(meta, meta_schema, params, _opts) do - doc_id = meta_schema - |> Map.get(:schema) - |> build_namespace() - |> namespace_id(params[:_id]) + doc_id = + meta_schema + |> Map.get(:schema) + |> build_namespace() + |> namespace_id(params[:_id]) Couchx.DbConnection.get(meta[:pid], doc_id) |> find_to_delete(meta[:pid], doc_id) @@ -565,8 +594,8 @@ defmodule Couchx.Adapter do @impl true def update(meta, repo, fields, identity, returning, _opts) do %{schema: schema} = repo - data = for {key, val} <- fields, into: %{}, do: {Atom.to_string(key), val} - doc_id = URI.encode_www_form(identity[:_id]) + data = for {key, val} <- fields, into: %{}, do: {Atom.to_string(key), val} + doc_id = URI.encode_www_form(identity[:_id]) {:ok, response} = Couchx.DbConnection.get(meta[:pid], doc_id) prev_fields = for {key, val} <- response, do: {String.to_atom(key), val} @@ -578,7 +607,7 @@ defmodule Couchx.Adapter do constraints = Constraint.call(meta[:pid], repo, fields, prev_fields) constraints - |> DocumentState.merge_constraints + |> DocumentState.merge_constraints() |> do_update(constraints, doc_id, prev_data, data, returning, meta[:pid]) end @@ -593,16 +622,17 @@ defmodule Couchx.Adapter do end def do_insert(errors, _, _, _, _, _) - when length(errors) > 0 do + when length(errors) > 0 do {:invalid, errors} end def do_insert(_errors, repo, constraints, fields, returning, meta) do - data = Enum.into(fields, %{}) - |> build_id(repo) - |> typed_document(repo) + data = + Enum.into(fields, %{}) + |> build_id(repo) + |> typed_document(repo) - url = URI.encode_www_form(data._id) + url = URI.encode_www_form(data._id) body = Jason.encode!(data) constraints @@ -628,14 +658,15 @@ defmodule Couchx.Adapter do values = Map.merge(data, %{_rev: response["rev"]}) values = fetch_insert_values(response, values, returning) {:ok, Enum.zip(returning, values)} + {:error, error} -> {:error, error} end end defp do_update(errors, _constraints, _id, _response, _data, _returning, _server) - when length(errors) > 0 do - {:invalid, errors} + when length(errors) > 0 do + {:invalid, errors} end defp do_update(_errors, constraints, doc_id, response, data, returning, server) do @@ -658,24 +689,26 @@ defmodule Couchx.Adapter do defp try_to_persist_update(%{ok: _}, doc_id, prev_data, returning, data, server) do values = Map.merge(prev_data, data) - body = Jason.encode!(values) + body = Jason.encode!(values) case Couchx.DbConnection.insert(server, doc_id, body) do {:ok, response} -> values = fetch_insert_values(response, values, returning) {:ok, Enum.zip(returning, values)} + {:error, error} -> {:error, error} end end defp fetch_insert_values(%{"ok" => true}, response, returning) do - data = case response do - %{_id: _id} -> response - _ -> for {key, val} <- response, into: %{}, do: {String.to_atom(key), val} - end + data = + case response do + %{_id: _id} -> response + _ -> for {key, val} <- response, into: %{}, do: {String.to_atom(key), val} + end - Enum.map(returning, fn(k)-> + Enum.map(returning, fn k -> Map.get(data, k) end) end @@ -713,13 +746,13 @@ defmodule Couchx.Adapter do defp unencoded_namespace_id(namespace, id) do namespace |> namespace_id(id) - |> URI.decode_www_form + |> URI.decode_www_form() end defp prepare_view_options(options) do @encodable_keys |> Enum.reduce(options, fn key, acc -> - Keyword.replace(acc, key, Jason.encode!(options[key])) + Keyword.replace(acc, key, Jason.encode!(options[key])) end) |> Enum.into(%{}) end diff --git a/lib/couchx/constraint.ex b/lib/couchx/constraint.ex index cf08ac3..769aa91 100644 --- a/lib/couchx/constraint.ex +++ b/lib/couchx/constraint.ex @@ -1,24 +1,28 @@ defmodule Couchx.Constraint do + @moduledoc """ + Service module to handle database constraints + """ + def call(server, repo, fields, prev_fields \\ []) def call( - server, - %{source: source, schema: schema}, - fields, - prev_fields - ) do - if with_schema?(schema) do - params = Enum.into(fields, %{}) - schema_struct = struct(schema.__struct__(), prev_fields) - changeset = schema.changeset(schema_struct, params) - fields = Keyword.merge(prev_fields, fields) - - changeset - |> Map.get(:constraints) - |> Enum.map(&process_constraints(&1, source, fields, server, prev_fields)) - else - [{:ok, true}] - end + server, + %{source: source, schema: schema}, + fields, + prev_fields + ) do + if with_schema?(schema) do + params = Enum.into(fields, %{}) + schema_struct = struct(schema.__struct__(), prev_fields) + changeset = schema.changeset(schema_struct, params) + fields = Keyword.merge(prev_fields, fields) + + changeset + |> Map.get(:constraints) + |> Enum.map(&process_constraints(&1, source, fields, server, prev_fields)) + else + [{:ok, true}] + end end def call(_server, _repo, _fields, _prev_fields) do @@ -30,12 +34,13 @@ defmodule Couchx.Constraint do end defp process_constraints( - %{constraint: constraint, type: :unique}, - source, - fields, - server, - prev_fields - ) when prev_fields == [] do + %{constraint: constraint, type: :unique}, + source, + fields, + server, + prev_fields + ) + when prev_fields == [] do unique_fields = constraint_to_fields(constraint) fields @@ -45,30 +50,38 @@ defmodule Couchx.Constraint do end defp process_constraints( - %{constraint: constraint, type: :unique} = constraints, - source, - fields, - server, - prev_fields - ) do + %{constraint: constraint, type: :unique} = constraints, + source, + fields, + server, + prev_fields + ) do unique_fields = constraint_to_fields(constraint) doc_id = unique_doc_id(fields, unique_fields, source) prev_doc_id = unique_doc_id(prev_fields, unique_fields, source) - if (doc_id == prev_doc_id) do + if doc_id == prev_doc_id do {:ok, true} else process_constraints(constraints, source, fields, server, []) end end - defp process_constraints(%{constraint: constraint, field: field, type: :foreign_key}, _source, fields, server, _prev_fields) do - doc_id = Keyword.get(fields, field) - |> URI.encode_www_form + defp process_constraints( + %{constraint: constraint, field: field, type: :foreign_key}, + _source, + fields, + server, + _prev_fields + ) do + doc_id = + Keyword.get(fields, field) + |> URI.encode_www_form() case Couchx.DbConnection.get(server, doc_id) do {:ok, _} -> {:ok, true} + {:error, "not_found :: " <> _reason} -> {:invalid, [foreign_key: constraint]} end @@ -86,7 +99,7 @@ defmodule Couchx.Constraint do |> List.delete(:index) end - defp validate_uniqueness(false, _, _, _, _server) do + defp validate_uniqueness(false, _, _, _, _server) do raise "All unique fields are required." end @@ -115,10 +128,11 @@ defmodule Couchx.Constraint do end defp unique_doc_id(fields, unique_fields, source) do - values = fields - |> Keyword.take(unique_fields) - |> Keyword.values - |> Enum.join("-") + values = + fields + |> Keyword.take(unique_fields) + |> Keyword.values() + |> Enum.join("-") "#{source}-#{values}" end diff --git a/lib/couchx/couch_id.ex b/lib/couchx/couch_id.ex index 349ad02..d389f09 100644 --- a/lib/couchx/couch_id.ex +++ b/lib/couchx/couch_id.ex @@ -1,4 +1,8 @@ defmodule Couchx.CouchId do + @moduledoc """ + Helper module to return doc information + """ + def base_id(id), do: String.replace(id, ~r{^([^/]+/)}, "") def underscore_school_id(school_id), do: String.replace(school_id, "/", "_") end diff --git a/lib/couchx/db_connection.ex b/lib/couchx/db_connection.ex index 4702c14..bc347d7 100644 --- a/lib/couchx/db_connection.ex +++ b/lib/couchx/db_connection.ex @@ -1,4 +1,8 @@ defmodule Couchx.DbConnection do + @moduledoc """ + Connection GenServer that handles all db server requests and responses + """ + use GenServer, restart: :transient def start_link(args) do @@ -13,7 +17,7 @@ defmodule Couchx.DbConnection do end def terminate(reason, _state) do - IO.inspect reason + IO.puts(inspect(reason)) end def info(server), do: GenServer.call(server, :info) @@ -75,16 +79,16 @@ defmodule Couchx.DbConnection do def handle_call({:index, doc}, _from, state) do headers = state[:base_headers] - url = "#{state[:base_url]}/_index" - body = Jason.encode!(doc) + url = "#{state[:base_url]}/_index" + body = Jason.encode!(doc) - request(:post, url, body, [headers: headers, options: []]) + request(:post, url, body, headers: headers, options: []) |> call_response(state) end def handle_call({:delete_admin, name}, _from, state) do - url = "#{state[:base_url]}/_users/org.couchdb.user:#{name}" - opts = [headers: state[:base_headers], options: state[:options]] + url = "#{state[:base_url]}/_users/org.couchdb.user:#{name}" + opts = [headers: state[:base_headers], options: state[:options]] user_doc = request(:get, url, opts) request(:delete, "#{url}?rev=#{user_doc["_rev"]}", opts) @@ -95,13 +99,14 @@ defmodule Couchx.DbConnection do opts = [headers: state[:base_headers], options: state[:options]] create_role(state[:base_url], name, name, opts) + create_admin_user(state[:base_url], name, password, opts) |> call_response(state) |> call_response(state) end def handle_call({:create_db, name}, _from, state) do - url = "#{state[:base_url]}/#{name}" + url = "#{state[:base_url]}/#{name}" opts = [headers: state[:base_headers], options: state[:options]] request(:put, url, [], opts) @@ -109,7 +114,7 @@ defmodule Couchx.DbConnection do end def handle_call({:delete, doc_id, rev}, _from, state) do - url = "#{state[:base_url]}/#{doc_id}?rev=#{rev}" + url = "#{state[:base_url]}/#{doc_id}?rev=#{rev}" opts = [headers: state[:base_headers], options: state[:options]] request(:delete, url, opts) @@ -117,7 +122,7 @@ defmodule Couchx.DbConnection do end def handle_call({:delete_db, name}, _from, state) do - url = "#{state[:base_url]}/#{name}" + url = "#{state[:base_url]}/#{name}" opts = [headers: state[:base_headers], options: state[:options]] request(:delete, url, opts) @@ -125,44 +130,43 @@ defmodule Couchx.DbConnection do end def handle_call(:info, _from, state) do - request(:get, state[:base_url], [headers: state[:base_headers], options: state[:options]]) + request(:get, state[:base_url], headers: state[:base_headers], options: state[:options]) |> call_response(state) end def handle_call({:all_docs, keys, options}, _from, state) do - headers = state[:base_headers] + headers = state[:base_headers] with_docs = options[:include_docs] || false - url = state[:base_url] <> "/_all_docs?include_docs=#{with_docs}" - body = Jason.encode!(%{keys: keys}) + url = state[:base_url] <> "/_all_docs?include_docs=#{with_docs}" + body = Jason.encode!(%{keys: keys}) - request(:post, url, body, [headers: headers, options: []]) + request(:post, url, body, headers: headers, options: []) |> call_response(state) - end def handle_call({:bulk_docs, docs, options}, _from, state) do headers = state[:base_headers] - url = state[:base_url] <> "/_bulk_docs" - body = Jason.encode!(%{docs: docs}) + url = state[:base_url] <> "/_bulk_docs" + body = Jason.encode!(%{docs: docs}) - request(:post, url, body, [headers: headers, options: options]) + request(:post, url, body, headers: headers, options: options) |> call_response(state) end def handle_call({:insert, resource, body, options}, _from, state) do - headers = state[:base_headers] - url = state[:base_url] <> "/#{resource}" + headers = state[:base_headers] + url = state[:base_url] <> "/#{resource}" - request(:put, url, body, [headers: headers, options: options]) + request(:put, url, body, headers: headers, options: options) |> call_response(state) end def handle_call({:get, resource, query, options}, _from, state) do - headers = state[:base_headers] + headers = state[:base_headers] query_str = build_query_str(query) - url = "#{state[:base_url]}/#{resource}#{query_str}" + url = "#{state[:base_url]}/#{resource}#{query_str}" - request(:get, url, [headers: headers, options: options]) + request(:get, url, headers: headers, options: options) |> call_response(state) end @@ -174,31 +178,32 @@ defmodule Couchx.DbConnection do case method do :get -> request(method, url, headers: state[:base_headers], options: req_options) + :delete -> request(:delete, url, headers: state[:base_headers], options: []) + _ -> body = Jason.encode!(options[:body]) request(method, url, body, headers: state[:base_headers], options: req_options) end |> call_response(state) - end def handle_call({:find, query, options}, _from, state) do - headers = state[:base_headers] + headers = state[:base_headers] query_str = build_query_str(options[:query_str]) - url = "#{state[:base_url]}/_find#{query_str}" - body = Jason.encode!(query) + url = "#{state[:base_url]}/_find#{query_str}" + body = Jason.encode!(query) - request(:post, url, body, [headers: headers, options: options]) + request(:post, url, body, headers: headers, options: options) |> call_response(state) end def handle_call({:delete_index, name, id}, _from, state) do - headers = state[:base_headers] - url = "#{state[:base_url]}/_index/_design/#{id}/json/#{name}" + headers = state[:base_headers] + url = "#{state[:base_url]}/_index/_design/#{id}/json/#{name}" - request(:delete, url, [headers: headers, options: []]) + request(:delete, url, headers: headers, options: []) |> call_response(state) end @@ -241,6 +246,7 @@ defmodule Couchx.DbConnection do defp call_response(response, state), do: {:reply, {:ok, response}, state} defp build_query_str(nil), do: "" + defp build_query_str(query) do "?#{URI.encode_query(query)}" end @@ -258,8 +264,9 @@ defmodule Couchx.DbConnection do end defp fetch_headers(config) do - credentials = "#{config[:username]}:#{config[:password]}" - |> Base.encode64() + credentials = + "#{config[:username]}:#{config[:password]}" + |> Base.encode64() [ {"Content-Type", "application/json"}, @@ -272,16 +279,18 @@ defmodule Couchx.DbConnection do end defp create_admin_user(base_url, name, password, opts) do - url = "#{base_url}/_users/org.couchdb.user:#{name}" - body = name - |> user_doc(password) - |> Jason.encode! + url = "#{base_url}/_users/org.couchdb.user:#{name}" + + body = + name + |> user_doc(password) + |> Jason.encode!() request(:put, url, body, opts) end defp create_role(base_url, db_name, name, opts) do - roles = %{members: %{ names: [], roles: [] }, admins: %{ names: [name], roles: [] } } + roles = %{members: %{names: [], roles: []}, admins: %{names: [name], roles: []}} request(:put, "#{base_url}/#{db_name}/_security", Jason.encode!(roles), opts) end @@ -295,6 +304,7 @@ defmodule Couchx.DbConnection do end defp process_name(nil), do: __MODULE__ + defp process_name(name) do {:via, Registry, {CouchxRegistry, name}} end diff --git a/lib/couchx/document_state.ex b/lib/couchx/document_state.ex index e9ed834..2d838a2 100644 --- a/lib/couchx/document_state.ex +++ b/lib/couchx/document_state.ex @@ -1,27 +1,32 @@ defmodule Couchx.DocumentState do + @moduledoc """ + Helper module adding constrains on documents before inserting docs to the database + """ + alias Couchx.DbConnection def merge_constraints(constraints) do constraints - |> Enum.filter(fn({state, _})-> state == :invalid end) - |> Keyword.values - |> List.flatten + |> Enum.filter(fn {state, _} -> state == :invalid end) + |> Keyword.values() + |> List.flatten() end def process_constraints([], _server), do: %{ok: true} def process_constraints(constraints, server) do - Enum.map(constraints, fn({:ok, constraint})-> + Enum.map(constraints, fn {:ok, constraint} -> case constraint do %{type: :unique} = constraint -> constraint_doc_id = URI.encode_www_form(constraint.id) - constraint_doc = %{_id: constraint.id, type: "constraint"} |> Jason.encode! + constraint_doc = %{_id: constraint.id, type: "constraint"} |> Jason.encode!() DbConnection.insert(server, constraint_doc_id, constraint_doc) + true -> {:ok, true} end end) - |> Enum.group_by(fn({state, _})-> state end) - |> Enum.reduce(%{}, fn({k, v}, acc)-> Map.put(acc, k, Keyword.values(v)) end) + |> Enum.group_by(fn {state, _} -> state end) + |> Enum.reduce(%{}, fn {k, v}, acc -> Map.put(acc, k, Keyword.values(v)) end) end end diff --git a/lib/couchx/dynamic_repo.ex b/lib/couchx/dynamic_repo.ex index c93a203..285c590 100644 --- a/lib/couchx/dynamic_repo.ex +++ b/lib/couchx/dynamic_repo.ex @@ -1,4 +1,9 @@ defmodule Couchx.DynamicRepo do + @moduledoc """ + Macro module to support dyamic repos for Couchx adapter. + Useful to handle multiple databases. + """ + defmacro __using__(otp_app: otp_app, name: name) do quote location: :keep do @otp_app unquote(otp_app) @@ -14,11 +19,13 @@ defmodule Couchx.DynamicRepo do end def with_dynamic_repo(name, credentials, callback) do - name = if (is_atom(name)), do: name, else: String.to_atom(name) + name = if is_atom(name), do: name, else: String.to_atom(name) default_dynamic_repo = get_dynamic_repo() start_opts = [name: name] ++ credentials - repo = __MODULE__.start_link(start_opts) - |> maybe_fetch_repo() + + repo = + __MODULE__.start_link(start_opts) + |> maybe_fetch_repo() try do __MODULE__.put_dynamic_repo(repo) diff --git a/lib/couchx/finders.ex b/lib/couchx/finders.ex index 7ae8d5d..18f4ae0 100644 --- a/lib/couchx/finders.ex +++ b/lib/couchx/finders.ex @@ -1,4 +1,8 @@ defmodule Couchx.Finders do + @moduledoc """ + Helper macro module to add find function to Ecto Repos. + """ + defmacro __using__(repo: repo, dynamic: is_dynamic) do quote location: :keep do @repo unquote(repo) diff --git a/lib/couchx/mango_index.ex b/lib/couchx/mango_index.ex index 8e940a1..4fcbc85 100644 --- a/lib/couchx/mango_index.ex +++ b/lib/couchx/mango_index.ex @@ -1,4 +1,8 @@ defmodule Couchx.MangoIndex do + @moduledoc """ + Macro to generate migration scripts to generate mango indexes + """ + defmacro __using__(repo_name: repo_name) do quote location: :keep do @repo_name unquote(repo_name) @@ -33,17 +37,18 @@ defmodule Couchx.MangoIndex do defp persist_index(doc) do repo = Ecto.Repo.Registry.lookup(@repo_name) + Couchx.DbConnection.index(repo.pid, doc) |> handle_response end defp delete_index(name, id \\ nil) do repo = Ecto.Repo.Registry.lookup(@repo_name) + Couchx.DbConnection.delete(repo.pid, :index, name, id) |> handle_response end - defp handle_response({:ok, response}), do: response defp handle_response({_, response}), do: {:error, response} end diff --git a/lib/couchx/migrator.ex b/lib/couchx/migrator.ex index 3c4d1ee..5187c18 100644 --- a/lib/couchx/migrator.ex +++ b/lib/couchx/migrator.ex @@ -1,10 +1,14 @@ defmodule Couchx.Migrator do + @moduledoc """ + Module to support mix migrate tasks + """ + import Couchx.Support.ApplicationHelper def run(repo, :up) do repo |> index_path - |> Path.wildcard + |> Path.wildcard() |> require_files |> Enum.map(&fetch_modules/1) |> run_direction(:up) @@ -46,7 +50,7 @@ defmodule Couchx.Migrator do defp migration_code(file_path) do file_path |> to_string - |> File.read! + |> File.read!() end defp migration_to_module(%{"module" => module}) do @@ -54,6 +58,7 @@ defmodule Couchx.Migrator do |> Kernel.<>(module) |> base_module_atom end + defp require_files(files) do Enum.each(files, &Code.require_file/1) files diff --git a/lib/couchx/prepare_query.ex b/lib/couchx/prepare_query.ex index 557ff0d..753376a 100644 --- a/lib/couchx/prepare_query.ex +++ b/lib/couchx/prepare_query.ex @@ -1,4 +1,8 @@ defmodule Couchx.PrepareQuery do + @moduledoc """ + Service module to parse SQL like queries into CouchDB valid request body/params + """ + @query_map [ ==: "$eq", or: "$or", @@ -20,7 +24,7 @@ defmodule Couchx.PrepareQuery do @operator_keys Keyword.keys(@operators) def call(%{wheres: wheres, limit: _limit} = query) do - keys = Enum.map(wheres, &parse_where/1) + keys = Enum.map(wheres, &parse_where/1) options = parse_options(query) [keys: keys, options: options] @@ -33,6 +37,7 @@ defmodule Couchx.PrepareQuery do end defp parse_where([]), do: [] + defp parse_where(%Ecto.Query.BooleanExpr{expr: expr}) do {condition, _, fields} = expr build_query_condition(condition, fields) @@ -41,18 +46,21 @@ defmodule Couchx.PrepareQuery do defp build_query_condition(condition, [{{_, [], [{_, [], [_]}, key]}, [], []}, value]) do cond do condition == :== -> - %{ key => value } + %{key => value} + condition == :in and key == :_id -> - %{ key => value } + %{key => value} + condition in @operator_keys -> - %{ key => %{ @operators[condition] => value } } + %{key => %{@operators[condition] => value}} + true -> {:error, "invalid query operator"} end end defp build_query_condition(condition, fields) do - %{ @query_map[condition] => build_query(fields) } + %{@query_map[condition] => build_query(fields)} end defp build_query(fields) do @@ -63,21 +71,21 @@ defmodule Couchx.PrepareQuery do defp build_field_condition({{_, _, [{_, _, [0]}, key]}, _, _}), do: %{key => :empty} defp build_field_condition({expr, _, [{{_, _, [{_, _, _}, key]}, _, _}, value]}) do - %{ key => %{ @query_map[expr] => value } } + %{key => %{@query_map[expr] => value}} end defp build_field_condition({expr, _, [{{_, _, [_, key]}, _, _}, value]}) do - %{ key => %{ @query_map[expr] => value } } + %{key => %{@query_map[expr] => value}} end defp build_field_condition({expr, _, list}) - when expr in ~w[== and]a and is_list(list) do + when expr in ~w[== and]a and is_list(list) do Enum.reduce(list, %{}, &Map.merge(&2, build_field_condition(&1))) end defp build_field_condition({expr, _, list}) - when expr == :or and is_list(list) do - %{"$or": [Enum.reduce(list, %{}, &Map.merge(&2, build_field_condition(&1)))] } + when expr == :or and is_list(list) do + %{"$or": [Enum.reduce(list, %{}, &Map.merge(&2, build_field_condition(&1)))]} end defp parse_options(%{order_bys: order_bys, limit: limit, offset: skip}) do @@ -88,18 +96,21 @@ defmodule Couchx.PrepareQuery do end defp try_add_limit(options, nil), do: options + defp try_add_limit(options, %{expr: limit}) do options |> Map.merge(%{limit: limit}) end defp try_add_order(opts, []), do: opts + defp try_add_order(opts, [%{expr: orders}]) do order = Enum.map(orders, &parse_orders/1) Map.merge(opts, %{sort: order}) end defp try_add_skip(options, nil), do: options + defp try_add_skip(options, %{expr: skip}) do options |> Map.merge(%{skip: skip}) diff --git a/lib/couchx/query_handler.ex b/lib/couchx/query_handler.ex index 12fe356..7cf6512 100644 --- a/lib/couchx/query_handler.ex +++ b/lib/couchx/query_handler.ex @@ -1,5 +1,9 @@ defmodule Couchx.QueryHandler do - @empty_response [ + @moduledoc """ + Helpers for handling query results from Couch Adapter + """ + + @empty_response [ %{"rows" => []}, %{"docs" => []}, %{"bookmark" => "nil", "docs" => []} @@ -8,7 +12,7 @@ defmodule Couchx.QueryHandler do def query_results([], _, _), do: {0, []} def query_results({:error, _reason}, _, _), do: {0, []} - def query_results([%{"_id" => _}|_] = docs, fields, metadata) do + def query_results([%{"_id" => _} | _] = docs, fields, metadata) do Enum.map(docs, &process_docs(&1, fields, metadata)) |> execute_response end @@ -18,7 +22,7 @@ defmodule Couchx.QueryHandler do end def query_results({:ok, response}, fields, metadata) - when is_list(response) do + when is_list(response) do Enum.map(response, &query_results(&1, fields, metadata)) |> execute_response end @@ -41,7 +45,7 @@ defmodule Couchx.QueryHandler do process_docs(doc, fields, metadata) end - def query_results(%{"ok" => true, "id"=> id, "rev"=> rev}, _fields, nil) do + def query_results(%{"ok" => true, "id" => id, "rev" => rev}, _fields, nil) do [_id: id, _rev: rev] end @@ -63,14 +67,14 @@ defmodule Couchx.QueryHandler do end defp process_docs(doc, fields, nil) do - Enum.reduce(fields, [], fn({key, value}, acc) -> + Enum.reduce(fields, [], fn {key, value}, acc -> confirmed_value = if doc[key], do: value, else: nil acc ++ confirmed_value end) end defp process_docs(doc, _fields, meta) do - Enum.reduce(meta, [], fn({key, type}, acc) -> + Enum.reduce(meta, [], fn {key, type}, acc -> value = Map.get(doc, to_string(key)) acc ++ [Ecto.Type.cast(type, value) |> elem(1)] end) diff --git a/lib/couchx/support/application_helper.ex b/lib/couchx/support/application_helper.ex index 52f6b3d..8af9a78 100644 --- a/lib/couchx/support/application_helper.ex +++ b/lib/couchx/support/application_helper.ex @@ -1,8 +1,12 @@ defmodule Couchx.Support.ApplicationHelper do + @moduledoc """ + Helper module with repo information tools + """ + def base_repo_path(repo, directory) do config = repo.config() - priv = config[:priv] || "priv/#{Macro.underscore(repo)}" - app = Keyword.fetch!(config, :otp_app) + priv = config[:priv] || "priv/#{Macro.underscore(repo)}" + app = Keyword.fetch!(config, :otp_app) Application.app_dir(app, Path.join(priv, directory)) end diff --git a/lib/mix/tasks/couchx.gen.mango_index.ex b/lib/mix/tasks/couchx.gen.mango_index.ex index 51a0c1e..659d760 100644 --- a/lib/mix/tasks/couchx.gen.mango_index.ex +++ b/lib/mix/tasks/couchx.gen.mango_index.ex @@ -1,11 +1,16 @@ defmodule Mix.Tasks.Couchx.Gen.MangoIndex do + @moduledoc """ + This module will generate Mago indexes for couchdb queries + """ + use Mix.Task + import Macro, only: [camelize: 1] import Mix.Generator import Mix.Ecto import Couchx.Support.ApplicationHelper - @cwd File.cwd! + @cwd File.cwd!() @impl true def run(args) do @@ -14,7 +19,7 @@ defmodule Mix.Tasks.Couchx.Gen.MangoIndex do [repo] = parse_repo(args) index_name = parse_index_name(args) - assigns= [ + assigns = [ repo_name: repo_name(repo), index_name: index_name, index_module: index_module(index_name), @@ -50,15 +55,15 @@ defmodule Mix.Tasks.Couchx.Gen.MangoIndex do "#{@cwd}/lib/templates/mango_index.exs.eex" end - defp parse_index_name(["-n"|t]) do + defp parse_index_name(["-n" | t]) do List.first(t) end - defp parse_index_name(["--name"|t]) do + defp parse_index_name(["--name" | t]) do List.first(t) end - defp parse_index_name([_|t]) do + defp parse_index_name([_ | t]) do parse_index_name(t) end @@ -66,15 +71,15 @@ defmodule Mix.Tasks.Couchx.Gen.MangoIndex do raise "Missing Index Name" end - defp parsed_fields(["-f"|t]) do + defp parsed_fields(["-f" | t]) do fields_to_sigil(t) end - defp parsed_fields(["--fields"|t]) do + defp parsed_fields(["--fields" | t]) do fields_to_sigil(t) end - defp parsed_fields([_|t]) do + defp parsed_fields([_ | t]) do parsed_fields(t) end diff --git a/lib/mix/tasks/couchx.mango_index.down.ex b/lib/mix/tasks/couchx.mango_index.down.ex index 20f1076..9b147cd 100644 --- a/lib/mix/tasks/couchx.mango_index.down.ex +++ b/lib/mix/tasks/couchx.mango_index.down.ex @@ -47,15 +47,15 @@ defmodule Mix.Tasks.Couchx.MangoIndex.Down do :ok end - defp fetch_migrations(["-n"|t], repo) do + defp fetch_migrations(["-n" | t], repo) do build_migration_modules(repo, t) end - defp fetch_migrations(["--names"|t], repo) do + defp fetch_migrations(["--names" | t], repo) do build_migration_modules(repo, t) end - defp fetch_migrations([_|t], repo) do + defp fetch_migrations([_ | t], repo) do fetch_migrations(t, repo) end diff --git a/lib/mix/tasks/couchx.mango_index.ex b/lib/mix/tasks/couchx.mango_index.ex index 8a5d412..4a150a9 100644 --- a/lib/mix/tasks/couchx.mango_index.ex +++ b/lib/mix/tasks/couchx.mango_index.ex @@ -44,7 +44,6 @@ defmodule Mix.Tasks.Couchx.MangoIndex do The task will look for paths for all the repos and process the indexes it finds. """ - @shortdoc "create indexes declared on priv/repo_path" @impl true @@ -68,7 +67,7 @@ defmodule Mix.Tasks.Couchx.MangoIndex do defp report_response([]), do: nil defp report_response({:error, response}) do - IO.puts "Error #{response}" + IO.puts("Error #{response}") end defp report_response({:ok, indexes, _}) do @@ -80,15 +79,17 @@ defmodule Mix.Tasks.Couchx.MangoIndex do case index do %{"result" => "exists"} = index -> state = if index["result"] == "exists", do: "already exists", else: index["result"] - IO.puts "\n==== Index #{index["name"]} #{state} =====" - IO.inspect index - IO.puts "========================\n" + IO.puts("\n==== Index #{index["name"]} #{state} =====") + IO.puts(inspect(index)) + IO.puts("========================\n") + {:error, state} -> - IO.puts "\n======================" - IO.puts state - IO.puts "======================\n" + IO.puts("\n======================") + IO.puts(state) + IO.puts("======================\n") + default -> - IO.inspect default + IO.puts(inspect(default)) end end end diff --git a/mix.exs b/mix.exs index f12f9cd..18bce1b 100644 --- a/mix.exs +++ b/mix.exs @@ -32,19 +32,20 @@ defmodule Couchx.MixProject do {:ex_doc, "~> 0.11", only: :dev}, {:httpoison, "~> 1.8"}, {:inflex, "~> 2.0.0"}, + {:credo, "~> 1.7"}, {:jason, "~>1.1"} ] end defp package do [ - files: ["lib", "mix.exs", "README.md"], - maintainers: ["Javier Guerra"], - licenses: ["MIT"], - links: %{ - "GitHub" => "https://github.com/javierg/couchx", - "Docs" => "https://hexdocs.pm/couchx" - } - ] + files: ["lib", "mix.exs", "README.md"], + maintainers: ["Javier Guerra"], + licenses: ["MIT"], + links: %{ + "GitHub" => "https://github.com/javierg/couchx", + "Docs" => "https://hexdocs.pm/couchx" + } + ] end end diff --git a/mix.lock b/mix.lock index fa34cf2..e955bcc 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,8 @@ %{ + "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, + "credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"}, "db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"}, "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "earmark": {:hex, :earmark, "1.4.46", "8c7287bd3137e99d26ae4643e5b7ef2129a260e3dcf41f251750cb4563c8fb81", [:mix], [], "hexpm", "798d86db3d79964e759ddc0c077d5eb254968ed426399fbf5a62de2b5ff8910a"}, @@ -8,6 +10,7 @@ "ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"}, "ecto_sql": {:hex, :ecto_sql, "3.11.3", "4eb7348ff8101fbc4e6bbc5a4404a24fecbe73a3372d16569526b0cf34ebc195", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.11.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e5f36e3d736b99c7fee3e631333b8394ade4bafe9d96d35669fca2d81c2be928"}, "ex_doc": {:hex, :ex_doc, "0.34.1", "9751a0419bc15bc7580c73fde506b17b07f6402a1e5243be9e0f05a68c723368", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "d441f1a86a235f59088978eff870de2e815e290e44a8bd976fe5d64470a4c9d2"}, + "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, "httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, From 752c1f40c88a0cc44e0998c57606ee99c044e845 Mon Sep 17 00:00:00 2001 From: javierg Date: Tue, 10 Jun 2025 12:05:34 -0700 Subject: [PATCH 2/2] add back Db Error --- lib/couchx/db_error.ex | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 lib/couchx/db_error.ex diff --git a/lib/couchx/db_error.ex b/lib/couchx/db_error.ex new file mode 100644 index 0000000..876923f --- /dev/null +++ b/lib/couchx/db_error.ex @@ -0,0 +1,5 @@ +defmodule Couchx.DbError do + @moduledoc false + + defexception message: "Couchx Error" +end