QlikElixir
View SourceAn Elixir client library for uploading CSV files to Qlik Cloud with comprehensive API support.
Features
- Upload CSV files to Qlik Cloud using multipart form data
- Support for both file path and binary content uploads
- Automatic overwrite handling with delete-and-retry logic
- File size validation (500MB limit)
- Comprehensive error handling with custom error types
- Support for multiple tenant configurations
- Built-in retry logic and configurable timeouts
- Full test coverage with mocked HTTP interactions
Installation
Add qlik_elixir
to your list of dependencies in mix.exs
:
def deps do
[
{:qlik_elixir, "~> 0.1.0"}
]
end
Then run:
mix deps.get
Configuration
Environment Variables
The simplest way to configure QlikElixir is through environment variables:
export QLIK_API_KEY="your-api-key"
export QLIK_TENANT_URL="https://your-tenant.qlikcloud.com"
export QLIK_CONNECTION_ID="your-connection-id" # Optional
Application Configuration
You can also configure it in your config/config.exs
:
config :qlik_elixir,
api_key: "your-api-key",
tenant_url: "https://your-tenant.qlikcloud.com",
connection_id: "your-connection-id" # Optional
Runtime Configuration
For runtime configuration or multiple tenants:
config = QlikElixir.new_config(
api_key: "different-key",
tenant_url: "https://other-tenant.qlikcloud.com",
connection_id: "space-123",
http_options: [timeout: 60_000] # 1 minute timeout
)
QlikElixir.upload_csv("data.csv", config: config)
Usage
Basic File Upload
# Upload a CSV file
{:ok, %{"id" => file_id}} = QlikElixir.upload_csv("path/to/data.csv")
# Upload with custom name
{:ok, file} = QlikElixir.upload_csv("data.csv", name: "renamed_file.csv")
# Upload with overwrite
{:ok, file} = QlikElixir.upload_csv("data.csv", overwrite: true)
# Upload to specific connection
{:ok, file} = QlikElixir.upload_csv("data.csv", connection_id: "space-123")
Upload Content Directly
# Create CSV content dynamically
csv_content = "id,name,email\n1,John Doe,john@example.com\n2,Jane Smith,jane@example.com"
# Upload the content
{:ok, file} = QlikElixir.upload_csv_content(csv_content, "users.csv")
# With options
{:ok, file} = QlikElixir.upload_csv_content(
csv_content,
"users.csv",
connection_id: "space-456",
overwrite: true
)
List Files
# List all files (up to 100)
{:ok, %{"data" => files, "total" => total}} = QlikElixir.list_files()
# With pagination
{:ok, result} = QlikElixir.list_files(limit: 20, offset: 40)
Check File Existence
# Check if a file exists by name
if QlikElixir.file_exists?("important_data.csv") do
IO.puts("File already exists!")
end
Find File by Name
case QlikElixir.find_file_by_name("sales_data.csv") do
{:ok, file} ->
IO.puts("Found file with ID: #{file["id"]}")
{:error, _} ->
IO.puts("File not found")
end
Delete Files
# Delete by file ID
case QlikElixir.delete_file("file-id-123") do
:ok ->
IO.puts("File deleted successfully")
{:error, error} ->
IO.puts("Failed to delete: #{error.message}")
end
Advanced Usage
Custom Configuration per Request
# Create a custom config for a specific tenant
eu_config = QlikElixir.new_config(
api_key: System.get_env("EU_QLIK_API_KEY"),
tenant_url: "https://eu-tenant.qlikcloud.com"
)
# Use it for specific operations
{:ok, file} = QlikElixir.upload_csv("eu_data.csv", config: eu_config)
{:ok, files} = QlikElixir.list_files(config: eu_config)
Error Handling
QlikElixir provides detailed error information:
case QlikElixir.upload_csv("data.csv") do
{:ok, file} ->
IO.puts("Uploaded successfully: #{file["id"]}")
{:error, %QlikElixir.Error{} = error} ->
case error.type do
:file_exists_error ->
IO.puts("File already exists. Use overwrite: true to replace it.")
:file_too_large ->
IO.puts("File is too large. Maximum size is 500MB.")
:authentication_error ->
IO.puts("Invalid API key. Please check your configuration.")
:validation_error ->
IO.puts("Validation failed: #{error.message}")
_ ->
IO.puts("Upload failed: #{error.message}")
end
end
Batch Operations
# Upload multiple files
files = ["data1.csv", "data2.csv", "data3.csv"]
results = Enum.map(files, fn file ->
case QlikElixir.upload_csv(file) do
{:ok, result} -> {:ok, file, result["id"]}
{:error, error} -> {:error, file, error}
end
end)
# Process results
Enum.each(results, fn
{:ok, file, id} -> IO.puts("✓ #{file} uploaded as #{id}")
{:error, file, error} -> IO.puts("✗ #{file} failed: #{error.message}")
end)
Progress Monitoring
For large file uploads, you might want to show progress:
defmodule UploadProgress do
def upload_with_progress(file_path) do
%{size: file_size} = File.stat!(file_path)
IO.puts("Uploading #{Path.basename(file_path)} (#{format_bytes(file_size)})...")
case QlikElixir.upload_csv(file_path) do
{:ok, result} ->
IO.puts("✓ Upload complete! File ID: #{result["id"]}")
{:ok, result}
{:error, error} ->
IO.puts("✗ Upload failed: #{error.message}")
{:error, error}
end
end
defp format_bytes(bytes) when bytes < 1024, do: "#{bytes} B"
defp format_bytes(bytes) when bytes < 1024 * 1024, do: "#{Float.round(bytes / 1024, 1)} KB"
defp format_bytes(bytes), do: "#{Float.round(bytes / (1024 * 1024), 1)} MB"
end
HTTP Options
You can customize HTTP behavior through configuration:
config = QlikElixir.new_config(
api_key: "your-key",
tenant_url: "https://your-tenant.qlikcloud.com",
http_options: [
timeout: :timer.minutes(10), # 10 minute timeout for large files
retry: :transient, # Retry on transient errors
max_retries: 5, # Maximum number of retries
retry_delay: fn n -> n * 2000 end # Exponential backoff
]
)
Error Types
QlikElixir defines the following error types:
:validation_error
- Invalid input parameters:upload_error
- General upload failure:authentication_error
- Invalid or missing API key:configuration_error
- Invalid configuration:file_exists_error
- File already exists (when overwrite is false):file_not_found
- File or resource not found:file_too_large
- File exceeds 500MB limit:network_error
- Network connectivity issues:unknown_error
- Unexpected errors
Testing
Run the test suite:
mix test
Run with coverage:
mix coveralls.html
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- Built with Req for HTTP client functionality
- Inspired by the Qlik Cloud API documentation
- Thanks to the Elixir community for the amazing ecosystem