ash-project / ash-project/ash

Proposal: Create Ash module to deal with `file` resources / uploads.

Open
#1,052 4 comments 2 reactions 0 assignees View on GitHub
enhancement
Dominant language
Elixir
Stars
2.5k
Forks
422
Avg merge
23h 26m
Merged PRs (30d)
46

Description

**Is your feature request related to a problem? Please describe.**
Ash is great. We're missing interacting with files! I'm open to contributing at the very minimum an MVP to solve this 😄

**Describe the solution you'd like**
Use [waffle_ecto](https://github.com/elixir-waffle/waffle_ecto) under the hood for new module `AshFile.Resource.

Create a composite type `{:file, MyApp.Uploader.Foo}`

With a composite type of `{:file, :uploader}`:
- we could provide mix task for file uploader definition creation based that would map to
waffle_ecto's `Uploader` resource definition with ash integration.
- allows us to centralize file upload logic that could be reused in other resources, e.g. `MyApp.Avatar.Type` across multiple resources
- rely much more on waffle_ecto work to get MVP ready, implementation could be as simple as a macro def and some mix tasks
- all `:uploader` types auto-added to custom ash types
- potential to embed as an `AshFile.Resource`, all arguments defined in relevant sections in related module, centralizing logic

**Describe alternatives you've considered**

With a inline singleton to an Ash.Resource:

Macro expansion could define sub-module `Uploaders` with embedded waffle_ecto resource,
with `use Ash.Resource, extensions: [Ash.File]` for 1-1 mapping and feature parity with `waffle_ecto`.

Definitely way more implementation work up-front to get MVP ready (for me), but seems more `ash`
design-compliant than composite type approach.

This would allow us to define a global `ash_files` extension api with default
options, override-able at attribute level.

E.g:

```elixir

defmodule MyApp.Accounts.User do
use Ash.Resource,
extensions: [Ash.File]

...

attributes do
...
attribute :avatar, :file, allow_nil?: false, constraints: [
storage: (:s3 | :local),
validate_file?: (:bool | :fn), # for form uploads
s3_opts: [
access_key_id: :string,
secret_access_key: :string,
bucket: :string,
asset_host: :string,
storage_dir: (:string | :fn)
storage_dir_prefix (:string | :fn)
async?: :bool,
acl: :enum
],
common_opts: [
accepts: ({:list :string} | :fn) # file type
filename: (:fn),
versions: ({:list :string}, :string, :fn)
...
],
local_opts: [
...
]
]
end
end
```

**Express the feature either with a change to resource syntax, or with a change to the resource interface**

Due to extent of change, I'd like to get to an agreement on API design before filling this section out if ok.

**Additional context**

I'm currently leaning towards the composite approach due to less time needed for
implementation, but the decision in the end is ultimately left up to @zachdaniel 😄

Contributor guide

Open the contributing guide

Research direction

Start by reading the proposed waffle_ecto integration and Ash.Resource extension and composite-type APIs. Compare the composite-type approach with the Ash.File extension and embedded-resource alternative described in the issue. Done means an agreed API design and an MVP plan for file resources or uploads, including any required mix tasks.

Written by the indexing model from the issue text.

Assessment

Tech stack
elixir
Domain
backend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.