QlikElixir.REST.Helpers (qlik_elixir v0.4.0)

View Source

Shared helper functions for REST API modules.

Provides common utilities for configuration handling, query building, and request body construction used across all REST endpoints.

Summary

Functions

Adds a parameter to a keyword list if the value is not nil.

Builds a request body map from params using provided field list.

Builds the full path with query string appended if present.

Builds a query string from options using a list of {api_key, opts_key} tuples.

Formats bytes into human readable string (B, KB, MB, GB).

Extracts configuration from options, falling back to default config.

Normalizes a repeatable query parameter into the comma-separated form Qlik expects, leaving scalars and nil untouched.

Normalizes a delete response to :ok on success.

Normalizes a get response, converting :file_not_found to :not_found.

Puts a key-value pair into a map if the value is not nil.

Validates that a file path points to a regular file.

Validates file size against maximum allowed (500MB).

Validates that filename ends with .csv extension.

Functions

add_param(params, key, value)

@spec add_param(keyword(), atom(), any()) :: keyword()

Adds a parameter to a keyword list if the value is not nil.

Examples

iex> QlikElixir.REST.Helpers.add_param([], :limit, 10)
[limit: 10]

iex> QlikElixir.REST.Helpers.add_param([], :limit, nil)
[]

build_body(params, field_keys)

@spec build_body(map(), [atom() | {String.t(), atom()}]) :: map()

Builds a request body map from params using provided field list.

Supports two formats:

  • Atom list: [:name, :description] - uses atom as both param key and API key
  • Tuple list: [{"apiKey", :param_key}] - maps param key to different API key

Examples

iex> params = %{name: "Test", description: "Desc"}
iex> QlikElixir.REST.Helpers.build_body(params, [:name, :description])
%{"name" => "Test", "description" => "Desc"}

iex> params = %{space_id: "abc"}
iex> QlikElixir.REST.Helpers.build_body(params, [{"spaceId", :space_id}])
%{"spaceId" => "abc"}

build_path(base_path, query)

@spec build_path(String.t(), String.t()) :: String.t()

Builds the full path with query string appended if present.

Examples

iex> QlikElixir.REST.Helpers.build_path("api/v1/apps", "limit=10")
"api/v1/apps?limit=10"

iex> QlikElixir.REST.Helpers.build_path("api/v1/apps", "")
"api/v1/apps"

build_query(opts, param_mappings, base_mappings \\ [limit: :limit, next: :next])

@spec build_query(keyword(), [{atom(), atom()}], [{atom(), atom()}]) :: String.t()

Builds a query string from options using a list of {api_key, opts_key} tuples.

Cursor-paginated endpoints — most of them — get limit and next for free. Pass base_mappings to override that for an endpoint that pages differently, as the report templates one does with skip.

List values are comma-joined, so a repeatable parameter can be given as a list.

Examples

iex> QlikElixir.REST.Helpers.build_query([limit: 10, space_id: "abc"], [{:spaceId, :space_id}])
"limit=10&spaceId=abc"

iex> QlikElixir.REST.Helpers.build_query([skip: 20], [{:skip, :skip}], [{:limit, :limit}])
"skip=20"

iex> QlikElixir.REST.Helpers.build_query([sort: ["+name", "-createdAt"]], [{:sort, :sort}])
"sort=%2Bname%2C-createdAt"

format_bytes(bytes)

@spec format_bytes(non_neg_integer()) :: String.t()

Formats bytes into human readable string (B, KB, MB, GB).

get_config(opts)

@spec get_config(keyword()) :: QlikElixir.Config.t()

Extracts configuration from options, falling back to default config.

Examples

iex> config = QlikElixir.Config.new(api_key: "key", tenant_url: "https://example.com")
iex> QlikElixir.REST.Helpers.get_config(config: config).api_key
"key"

join_param(values)

@spec join_param(nil | String.t() | list()) :: String.t() | nil

Normalizes a repeatable query parameter into the comma-separated form Qlik expects, leaving scalars and nil untouched.

Examples

iex> QlikElixir.REST.Helpers.join_param(["+name", "-updatedAt"])
"+name,-updatedAt"

iex> QlikElixir.REST.Helpers.join_param("+name")
"+name"

iex> QlikElixir.REST.Helpers.join_param(nil)
nil

normalize_delete_response(error, resource_name)

@spec normalize_delete_response({:ok, any()} | {:error, any()}, String.t()) ::
  :ok | {:error, any()}

Normalizes a delete response to :ok on success.

Converts :file_not_found errors to :not_found with a custom message.

Examples

iex> QlikElixir.REST.Helpers.normalize_delete_response({:ok, %{}}, "User")
:ok

iex> {:error, error} = QlikElixir.REST.Helpers.normalize_delete_response({:error, %QlikElixir.Error{type: :file_not_found}}, "User")
iex> {error.type, error.message}
{:not_found, "User not found"}

normalize_get_response(success, resource_name)

@spec normalize_get_response({:ok, any()} | {:error, any()}, String.t()) ::
  {:ok, any()} | {:error, any()}

Normalizes a get response, converting :file_not_found to :not_found.

Examples

iex> QlikElixir.REST.Helpers.normalize_get_response({:ok, %{"id" => "123"}}, "User")
{:ok, %{"id" => "123"}}

iex> {:error, error} = QlikElixir.REST.Helpers.normalize_get_response({:error, %QlikElixir.Error{type: :file_not_found}}, "User")
iex> {error.type, error.message}
{:not_found, "User not found"}

put_if_present(map, key, value)

@spec put_if_present(map(), String.t(), any()) :: map()

Puts a key-value pair into a map if the value is not nil.

Examples

iex> QlikElixir.REST.Helpers.put_if_present(%{}, "name", "test")
%{"name" => "test"}

iex> QlikElixir.REST.Helpers.put_if_present(%{}, "name", nil)
%{}

validate_file(file_path)

@spec validate_file(String.t()) ::
  {:ok, File.Stat.t()} | {:error, QlikElixir.Error.t()}

Validates that a file path points to a regular file.

Examples

iex> {:ok, stat} = QlikElixir.REST.Helpers.validate_file("mix.exs")
iex> stat.type
:regular

iex> {:error, error} = QlikElixir.REST.Helpers.validate_file("/nonexistent")
iex> error.type
:file_not_found

validate_file_size(size)

@spec validate_file_size(non_neg_integer()) :: :ok | {:error, QlikElixir.Error.t()}

Validates file size against maximum allowed (500MB).

validate_filename(filename)

@spec validate_filename(String.t()) :: :ok | {:error, QlikElixir.Error.t()}

Validates that filename ends with .csv extension.