Proposal: Create Ash module to deal with `file` resources / uploads.
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 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
Uploaderresource definition with ash integration. - allows us to centralize file upload logic that could be reused in other resources, e.g.
MyApp.Avatar.Typeacross 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
:uploadertypes 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:
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 😄
I love this, but I think it should be added as a configurable extension with multiple strategies ala ash authentication.
@jimsynz could you say more? Waffle ecto allows for s3 and local - if you are referring to multiple storage providers.
If you are referring to blob datatypes, I wonder if that would better fall under repos like ash_postgres.
This at the very least could be an interim tool.
What other strategies would you like to see?
Its interesting...if it can be encapsulated with a type then it should probably be done fully within the bounds of a type as opposed to an extension, but we'll really have to see how it takes shape.
I think let's start with the type-first approach you've got here, and see how far it can go. For now, let's call it ash_waffle, as opposed to ash_file or ash_file_upload or w/e. If we determine that this is the defecto strategy for a certain type of file uploads, we can make ash_file or ash_file_upload depend on this package, for example.