mirror of
https://github.com/ash-project/ash_archival.git
synced 2024-09-19 13:02:55 +12:00
improvement: create archival extension
This commit is contained in:
commit
11a8c81e5f
21 changed files with 1002 additions and 0 deletions
21
.check.exs
Normal file
21
.check.exs
Normal file
|
@ -0,0 +1,21 @@
|
|||
[
|
||||
## all available options with default values (see `mix check` docs for description)
|
||||
# parallel: true,
|
||||
# skipped: true,
|
||||
|
||||
## list of tools (see `mix check` docs for defaults)
|
||||
retry: false,
|
||||
tools: [
|
||||
## curated tools may be disabled (e.g. the check for compilation warnings)
|
||||
# {:compiler, false},
|
||||
|
||||
## ...or adjusted (e.g. use one-line formatter for more compact credo output)
|
||||
# {:credo, "mix credo --format oneline"},
|
||||
|
||||
{:check_formatter, command: "mix ash.formatter --check"},
|
||||
## custom new tools may be added (mix tasks or arbitrary commands)
|
||||
# {:my_mix_task, command: "mix release", env: %{"MIX_ENV" => "prod"}},
|
||||
# {:my_arbitrary_tool, command: "npm test", cd: "assets"},
|
||||
# {:my_arbitrary_script, command: ["my_script", "argument with spaces"], cd: "scripts"}
|
||||
]
|
||||
]
|
184
.credo.exs
Normal file
184
.credo.exs
Normal file
|
@ -0,0 +1,184 @@
|
|||
# This file contains the configuration for Credo and you are probably reading
|
||||
# this after creating it with `mix credo.gen.config`.
|
||||
#
|
||||
# If you find anything wrong or unclear in this file, please report an
|
||||
# issue on GitHub: https://github.com/rrrene/credo/issues
|
||||
#
|
||||
%{
|
||||
#
|
||||
# You can have as many configs as you like in the `configs:` field.
|
||||
configs: [
|
||||
%{
|
||||
#
|
||||
# Run any config using `mix credo -C <name>`. If no config name is given
|
||||
# "default" is used.
|
||||
#
|
||||
name: "default",
|
||||
#
|
||||
# These are the files included in the analysis:
|
||||
files: %{
|
||||
#
|
||||
# You can give explicit globs or simply directories.
|
||||
# In the latter case `**/*.{ex,exs}` will be used.
|
||||
#
|
||||
included: [
|
||||
"lib/",
|
||||
"src/",
|
||||
"test/",
|
||||
"web/",
|
||||
"apps/*/lib/",
|
||||
"apps/*/src/",
|
||||
"apps/*/test/",
|
||||
"apps/*/web/"
|
||||
],
|
||||
excluded: [~r"/_build/", ~r"/deps/", ~r"/node_modules/"]
|
||||
},
|
||||
#
|
||||
# Load and configure plugins here:
|
||||
#
|
||||
plugins: [],
|
||||
#
|
||||
# If you create your own checks, you must specify the source files for
|
||||
# them here, so they can be loaded by Credo before running the analysis.
|
||||
#
|
||||
requires: [],
|
||||
#
|
||||
# If you want to enforce a style guide and need a more traditional linting
|
||||
# experience, you can change `strict` to `true` below:
|
||||
#
|
||||
strict: false,
|
||||
#
|
||||
# To modify the timeout for parsing files, change this value:
|
||||
#
|
||||
parse_timeout: 5000,
|
||||
#
|
||||
# If you want to use uncolored output by default, you can change `color`
|
||||
# to `false` below:
|
||||
#
|
||||
color: true,
|
||||
#
|
||||
# You can customize the parameters of any check by adding a second element
|
||||
# to the tuple.
|
||||
#
|
||||
# To disable a check put `false` as second element:
|
||||
#
|
||||
# {Credo.Check.Design.DuplicatedCode, false}
|
||||
#
|
||||
checks: [
|
||||
#
|
||||
## Consistency Checks
|
||||
#
|
||||
{Credo.Check.Consistency.ExceptionNames, []},
|
||||
{Credo.Check.Consistency.LineEndings, []},
|
||||
{Credo.Check.Consistency.ParameterPatternMatching, []},
|
||||
{Credo.Check.Consistency.SpaceAroundOperators, false},
|
||||
{Credo.Check.Consistency.SpaceInParentheses, []},
|
||||
{Credo.Check.Consistency.TabsOrSpaces, []},
|
||||
|
||||
#
|
||||
## Design Checks
|
||||
#
|
||||
# You can customize the priority of any check
|
||||
# Priority values are: `low, normal, high, higher`
|
||||
#
|
||||
{Credo.Check.Design.AliasUsage, false},
|
||||
# You can also customize the exit_status of each check.
|
||||
# If you don't want TODO comments to cause `mix credo` to fail, just
|
||||
# set this value to 0 (zero).
|
||||
#
|
||||
{Credo.Check.Design.TagTODO, false},
|
||||
{Credo.Check.Design.TagFIXME, []},
|
||||
|
||||
#
|
||||
## Readability Checks
|
||||
#
|
||||
{Credo.Check.Readability.AliasOrder, []},
|
||||
{Credo.Check.Readability.FunctionNames, []},
|
||||
{Credo.Check.Readability.LargeNumbers, []},
|
||||
{Credo.Check.Readability.MaxLineLength, [priority: :low, max_length: 120]},
|
||||
{Credo.Check.Readability.ModuleAttributeNames, []},
|
||||
{Credo.Check.Readability.ModuleDoc, []},
|
||||
{Credo.Check.Readability.ModuleNames, []},
|
||||
{Credo.Check.Readability.ParenthesesInCondition, false},
|
||||
{Credo.Check.Readability.ParenthesesOnZeroArityDefs, []},
|
||||
{Credo.Check.Readability.PredicateFunctionNames, []},
|
||||
{Credo.Check.Readability.PreferImplicitTry, []},
|
||||
{Credo.Check.Readability.RedundantBlankLines, []},
|
||||
{Credo.Check.Readability.Semicolons, []},
|
||||
{Credo.Check.Readability.SpaceAfterCommas, []},
|
||||
{Credo.Check.Readability.StringSigils, []},
|
||||
{Credo.Check.Readability.TrailingBlankLine, []},
|
||||
{Credo.Check.Readability.TrailingWhiteSpace, []},
|
||||
{Credo.Check.Readability.UnnecessaryAliasExpansion, []},
|
||||
{Credo.Check.Readability.VariableNames, []},
|
||||
|
||||
#
|
||||
## Refactoring Opportunities
|
||||
#
|
||||
{Credo.Check.Refactor.CondStatements, []},
|
||||
{Credo.Check.Refactor.CyclomaticComplexity, [max_complexity: 19]},
|
||||
{Credo.Check.Refactor.FunctionArity, []},
|
||||
{Credo.Check.Refactor.LongQuoteBlocks, []},
|
||||
{Credo.Check.Refactor.MapInto, []},
|
||||
{Credo.Check.Refactor.MatchInCondition, []},
|
||||
{Credo.Check.Refactor.NegatedConditionsInUnless, []},
|
||||
{Credo.Check.Refactor.NegatedConditionsWithElse, []},
|
||||
{Credo.Check.Refactor.Nesting, [max_nesting: 3]},
|
||||
{Credo.Check.Refactor.UnlessWithElse, []},
|
||||
{Credo.Check.Refactor.WithClauses, []},
|
||||
|
||||
#
|
||||
## Warnings
|
||||
#
|
||||
{Credo.Check.Warning.BoolOperationOnSameValues, []},
|
||||
{Credo.Check.Warning.ExpensiveEmptyEnumCheck, []},
|
||||
{Credo.Check.Warning.IExPry, []},
|
||||
{Credo.Check.Warning.IoInspect, []},
|
||||
{Credo.Check.Warning.LazyLogging, []},
|
||||
{Credo.Check.Warning.MixEnv, false},
|
||||
{Credo.Check.Warning.OperationOnSameValues, []},
|
||||
{Credo.Check.Warning.OperationWithConstantResult, []},
|
||||
{Credo.Check.Warning.RaiseInsideRescue, []},
|
||||
{Credo.Check.Warning.UnusedEnumOperation, []},
|
||||
{Credo.Check.Warning.UnusedFileOperation, []},
|
||||
{Credo.Check.Warning.UnusedKeywordOperation, []},
|
||||
{Credo.Check.Warning.UnusedListOperation, []},
|
||||
{Credo.Check.Warning.UnusedPathOperation, []},
|
||||
{Credo.Check.Warning.UnusedRegexOperation, []},
|
||||
{Credo.Check.Warning.UnusedStringOperation, []},
|
||||
{Credo.Check.Warning.UnusedTupleOperation, []},
|
||||
{Credo.Check.Warning.UnsafeExec, []},
|
||||
|
||||
#
|
||||
# Checks scheduled for next check update (opt-in for now, just replace `false` with `[]`)
|
||||
|
||||
#
|
||||
# Controversial and experimental checks (opt-in, just replace `false` with `[]`)
|
||||
#
|
||||
{Credo.Check.Readability.StrictModuleLayout, false},
|
||||
{Credo.Check.Consistency.MultiAliasImportRequireUse, false},
|
||||
{Credo.Check.Consistency.UnusedVariableNames, false},
|
||||
{Credo.Check.Design.DuplicatedCode, false},
|
||||
{Credo.Check.Readability.AliasAs, false},
|
||||
{Credo.Check.Readability.MultiAlias, false},
|
||||
{Credo.Check.Readability.Specs, false},
|
||||
{Credo.Check.Readability.SinglePipe, false},
|
||||
{Credo.Check.Readability.WithCustomTaggedTuple, false},
|
||||
{Credo.Check.Refactor.ABCSize, false},
|
||||
{Credo.Check.Refactor.AppendSingleItem, false},
|
||||
{Credo.Check.Refactor.DoubleBooleanNegation, false},
|
||||
{Credo.Check.Refactor.ModuleDependencies, false},
|
||||
{Credo.Check.Refactor.NegatedIsNil, false},
|
||||
{Credo.Check.Refactor.PipeChainStart, false},
|
||||
{Credo.Check.Refactor.VariableRebinding, false},
|
||||
{Credo.Check.Warning.LeakyEnvironment, false},
|
||||
{Credo.Check.Warning.MapGetUnsafePass, false},
|
||||
{Credo.Check.Warning.UnsafeToAtom, false}
|
||||
|
||||
#
|
||||
# Custom checks can be created using `mix credo.gen.check`.
|
||||
#
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
11
.formatter.exs
Normal file
11
.formatter.exs
Normal file
|
@ -0,0 +1,11 @@
|
|||
# THIS FILE IS AUTOGENERATED USING `mix ash.formatter`
|
||||
# DONT MODIFY IT BY HAND
|
||||
locals_without_parens = [archive_related: 1]
|
||||
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
|
||||
locals_without_parens: locals_without_parens,
|
||||
export: [
|
||||
locals_without_parens: locals_without_parens
|
||||
]
|
||||
]
|
76
.github/CODE_OF_CONDUCT.md
vendored
Normal file
76
.github/CODE_OF_CONDUCT.md
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at zach@zachdaniel.dev. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
2
.github/CONTRIBUTING.md
vendored
Normal file
2
.github/CONTRIBUTING.md
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Contributing Guidelines
|
||||
Contributing guidelines can be found in the core project, [ash](https://github.com/ash-project/ash/blob/main/.github/CONTRIBUTING.md)
|
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
27
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug, needs review
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is. If you are not sure if the bug is related to `ash` or an extension, log it with [ash](https://github.com/ash-project/ash/issues/new) and we will move it.
|
||||
|
||||
**To Reproduce**
|
||||
A minimal set of resource definitions and calls that can reproduce the bug.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
** Runtime
|
||||
- Elixir version
|
||||
- Erlang version
|
||||
- OS
|
||||
- Ash version
|
||||
- any related extension versions
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
36
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
36
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
name: Proposal
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement, needs review
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Express the feature either with a change to resource syntax, or with a change to the resource interface**
|
||||
|
||||
For example
|
||||
|
||||
```elixir
|
||||
attributes do
|
||||
attribute :foo, :integer, bar: 10 # <- Adding `bar` here would cause <x>
|
||||
end
|
||||
```
|
||||
|
||||
Or
|
||||
|
||||
```elixir
|
||||
Api.read(:resource, bar: 10) # <- Adding `bar` here would cause <x>
|
||||
```
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
### Contributor checklist
|
||||
|
||||
- [ ] Bug fixes include regression tests
|
||||
- [ ] Features include unit/acceptance tests
|
95
.github/workflows/elixir.yml
vendored
Normal file
95
.github/workflows/elixir.yml
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
name: Elixir CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags-ignore: ["v*"]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
create:
|
||||
branches: main
|
||||
tags: ["v*"]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
name: OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}} / Ash ${{matrix.ash}}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
otp: ["23"]
|
||||
elixir: ["1.13.2"]
|
||||
ash: ["main", "1.52.0-rc.1"]
|
||||
pg_version: ["9.6", "11", "12", "13"]
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ASH_VERSION: ${{matrix.ash}}
|
||||
steps:
|
||||
- run: sudo apt-get install --yes erlang-dev
|
||||
- uses: actions/checkout@v2
|
||||
- uses: erlef/setup-elixir@v1
|
||||
with:
|
||||
otp-version: ${{matrix.otp}}
|
||||
elixir-version: ${{matrix.elixir}}
|
||||
- uses: actions/cache@v2
|
||||
id: cache-deps
|
||||
with:
|
||||
path: deps
|
||||
key: otp-${{matrix.otp}}-elixir-${{matrix.elixir}}-deps-2-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
|
||||
restore-keys: otp-${{matrix.otp}}-elixir-${{matrix.elixir}}-deps-2
|
||||
- uses: actions/cache@v2
|
||||
id: cache-build
|
||||
with:
|
||||
path: _build
|
||||
key: otp-${{matrix.otp}}-elixir-${{matrix.elixir}}-build-2-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
|
||||
restore-keys: otp-${{matrix.otp}}-elixir-${{matrix.elixir}}-build-2
|
||||
- run: mix deps.get
|
||||
- run: mix check --except dialyzer
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
- run: mix check
|
||||
if: "!startsWith(github.ref, 'refs/tags/v')"
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
name: Release
|
||||
needs: [build]
|
||||
strategy:
|
||||
matrix:
|
||||
otp: ["23"]
|
||||
elixir: ["1.13.2"]
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- run: sudo apt-get install --yes erlang-dev
|
||||
- uses: actions/checkout@v2
|
||||
- uses: erlef/setup-elixir@v1
|
||||
with:
|
||||
otp-version: ${{matrix.otp}}
|
||||
elixir-version: ${{matrix.elixir}}
|
||||
- uses: actions/cache@v2
|
||||
id: cache-deps
|
||||
with:
|
||||
path: deps
|
||||
key: otp-${{matrix.otp}}-elixir-${{matrix.elixir}}-deps-2-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
|
||||
restore-keys: otp-${{matrix.otp}}-elixir-${{matrix.elixir}}-deps-2
|
||||
- run: mix deps.get
|
||||
- run: mix compile
|
||||
- run: mix hex.publish --yes
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
env:
|
||||
HEX_API_KEY: ${{ secrets.HEX_API_KEY }}
|
||||
- uses: little-core-labs/get-git-tag@v3.0.1
|
||||
id: tagName
|
||||
- uses: ethomson/send-tweet-action@v1
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
with:
|
||||
status: |
|
||||
Beta version "${{ steps.tagName.outputs.tag }}" of AshArchival released!
|
||||
|
||||
#myelixirstatus
|
||||
|
||||
See the changelog for more info:
|
||||
https://github.com/ash-project/ash_archival/blob/main/CHANGELOG.md
|
||||
consumer-key: ${{ secrets.TWITTER_CONSUMER_API_KEY }}
|
||||
consumer-secret: ${{ secrets.TWITTER_CONSUMER_API_SECRET }}
|
||||
access-token: ${{ secrets.TWITTER_ACCESS_TOKEN }}
|
||||
access-token-secret: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
|
26
.gitignore
vendored
Normal file
26
.gitignore
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
# The directory Mix will write compiled artifacts to.
|
||||
/_build/
|
||||
|
||||
# If you run "mix test --cover", coverage assets end up here.
|
||||
/cover/
|
||||
|
||||
# The directory Mix downloads your dependencies sources to.
|
||||
/deps/
|
||||
|
||||
# Where third-party dependencies like ExDoc output generated docs.
|
||||
/doc/
|
||||
|
||||
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||
/.fetch
|
||||
|
||||
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||
erl_crash.dump
|
||||
|
||||
# Also ignore archive artifacts (built via "mix archive.build").
|
||||
*.ez
|
||||
|
||||
# Ignore package tarball (built via "mix hex.build").
|
||||
archival-*.tar
|
||||
|
||||
# Temporary files, for example, from tests.
|
||||
/tmp/
|
7
.vscode/settings.json
vendored
Normal file
7
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"workbench.colorCustomizations": {
|
||||
"activityBar.background": "#342458",
|
||||
"titleBar.activeBackground": "#49327B",
|
||||
"titleBar.activeForeground": "#FCFBFD"
|
||||
}
|
||||
}
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 Zachary Scott Daniel
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
20
README.md
Normal file
20
README.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
# Ash Archival
|
||||
|
||||
A small but useful resource extension for [Ash Framework](https://github.com/ash-project/ash).
|
||||
|
||||
## Installation
|
||||
|
||||
The package can be installed by adding `ash_archival` to your list of dependencies in `mix.exs`:
|
||||
|
||||
```elixir
|
||||
def deps do
|
||||
[
|
||||
{:archival, "~> 0.1.0"}
|
||||
]
|
||||
end
|
||||
```
|
||||
|
||||
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
|
||||
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
|
||||
be found at <https://hexdocs.pm/archival>.
|
||||
|
5
lib/ash_archival.ex
Normal file
5
lib/ash_archival.ex
Normal file
|
@ -0,0 +1,5 @@
|
|||
defmodule AshArchival do
|
||||
@moduledoc """
|
||||
Documentation for `AshArchival`.
|
||||
"""
|
||||
end
|
37
lib/ash_archival/resource/changes/archive_related.ex
Normal file
37
lib/ash_archival/resource/changes/archive_related.ex
Normal file
|
@ -0,0 +1,37 @@
|
|||
defmodule AshArchival.Resource.Changes.ArchiveRelated do
|
||||
@moduledoc "Archives any related items configured to be archived"
|
||||
use Ash.Resource.Change
|
||||
require Ash.Query
|
||||
|
||||
def change(changeset, _, _) do
|
||||
archive_related = AshArchival.Resource.archive_related(changeset.resource)
|
||||
|
||||
if Enum.empty?(archive_related) do
|
||||
changeset
|
||||
else
|
||||
Ash.Changeset.after_action(changeset, fn changeset, result ->
|
||||
# This is not optimized. We should do this with bulk queries, not resource actions.
|
||||
loaded = changeset.api.load!(result, archive_related)
|
||||
|
||||
notifications =
|
||||
Enum.flat_map(archive_related, fn relationship ->
|
||||
relationship = Ash.Resource.Info.relationship(changeset.resource, relationship)
|
||||
|
||||
destroy_action =
|
||||
Ash.Resource.Info.primary_action!(relationship.destination, :destroy).name
|
||||
|
||||
loaded
|
||||
|> Map.get(relationship.name)
|
||||
|> List.wrap()
|
||||
|> Enum.flat_map(fn related ->
|
||||
related
|
||||
|> Ash.Changeset.for_destroy(destroy_action)
|
||||
|> (relationship.api || changeset.api).destroy!(return_notifications?: true)
|
||||
end)
|
||||
end)
|
||||
|
||||
{:ok, result, notifications}
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
34
lib/ash_archival/resource/resource.ex
Normal file
34
lib/ash_archival/resource/resource.ex
Normal file
|
@ -0,0 +1,34 @@
|
|||
defmodule AshArchival.Resource do
|
||||
@moduledoc """
|
||||
Configures a resource to be archived instead of destroyed for all destroy actions.
|
||||
|
||||
What does this resource extension do?
|
||||
|
||||
1. Adds a private `archived_at` `utc_datetime_usec` attribute.
|
||||
1. Marks all
|
||||
"""
|
||||
|
||||
@archive %Ash.Dsl.Section{
|
||||
name: :archive,
|
||||
describe: "A section for configuring how archival is configured for a resource.",
|
||||
schema: [
|
||||
archive_related: [
|
||||
type: {:list, :atom},
|
||||
doc: """
|
||||
A list of relationships that should have all related items archived when this is archived.
|
||||
Note: this is currently not optimized. It simply reads the relationship and archives each one
|
||||
(by calling its primary destroy, so the related resource must also use the archival extension).
|
||||
When bulk actions are supported by Ash then this can be updated to use those.
|
||||
"""
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
use Ash.Dsl.Extension,
|
||||
sections: [@archive],
|
||||
transformers: [AshArchival.Resource.Transformers.SetupArchival]
|
||||
|
||||
def archive_related(resource) do
|
||||
Ash.Dsl.Extension.get_opt(resource, [:archive], :archive_related, [])
|
||||
end
|
||||
end
|
116
lib/ash_archival/resource/transformers/setup_archival.ex
Normal file
116
lib/ash_archival/resource/transformers/setup_archival.ex
Normal file
|
@ -0,0 +1,116 @@
|
|||
defmodule AshArchival.Resource.Transformers.SetupArchival do
|
||||
@moduledoc "Sets up the required resource structure for archival"
|
||||
use Ash.Dsl.Transformer
|
||||
|
||||
@after_transformers [
|
||||
Ash.Resource.Transformers.ValidatePrimaryActions
|
||||
]
|
||||
|
||||
@before_transformers [
|
||||
Ash.Resource.Transformers.DefaultAccept,
|
||||
Ash.Resource.Transformers.SetTypes
|
||||
]
|
||||
|
||||
alias Ash.Dsl.Transformer
|
||||
|
||||
def transform(resource, dsl_state) do
|
||||
if Ash.Resource.Info.embedded?(resource) do
|
||||
{:ok, dsl_state}
|
||||
else
|
||||
dsl_state
|
||||
|> add_archived_at()
|
||||
|> update_destroy_actions()
|
||||
|> add_base_filter()
|
||||
|> add_base_filter_sql()
|
||||
end
|
||||
end
|
||||
|
||||
def after?(transformer) when transformer in @after_transformers, do: true
|
||||
def after?(_), do: false
|
||||
|
||||
def before?(transformer) when transformer in @before_transformers, do: true
|
||||
def before?(_), do: false
|
||||
|
||||
defp add_archived_at(dsl_state) do
|
||||
with {:ok, archived_at} <-
|
||||
Transformer.build_entity(Ash.Resource.Dsl, [:attributes], :attribute,
|
||||
name: :archived_at,
|
||||
type: :utc_datetime_usec,
|
||||
private?: true,
|
||||
allow_nil?: true
|
||||
) do
|
||||
{:ok, Transformer.add_entity(dsl_state, [:attributes], archived_at)}
|
||||
end
|
||||
end
|
||||
|
||||
defp update_destroy_actions({:ok, dsl_state}) do
|
||||
dsl_state
|
||||
|> Transformer.get_entities([:actions])
|
||||
|> Enum.filter(&(&1.type == :destroy))
|
||||
|> Enum.reduce({:ok, dsl_state}, fn destroy_action, {:ok, dsl_state} ->
|
||||
with {:ok, set_archived_at} <-
|
||||
Transformer.build_entity(Ash.Resource.Dsl, [:actions, :destroy], :change,
|
||||
change:
|
||||
Ash.Resource.Change.Builtins.set_attribute(:archived_at, &DateTime.utc_now/0)
|
||||
),
|
||||
{:ok, archive_related} <-
|
||||
Transformer.build_entity(Ash.Resource.Dsl, [:actions, :destroy], :change,
|
||||
change: {AshArchival.Resource.Changes.ArchiveRelated, []}
|
||||
) do
|
||||
new_action = %{
|
||||
destroy_action
|
||||
| soft?: true,
|
||||
changes: [set_archived_at, archive_related | destroy_action.changes]
|
||||
}
|
||||
|
||||
{:ok,
|
||||
Transformer.replace_entity(
|
||||
dsl_state,
|
||||
[:actions],
|
||||
new_action,
|
||||
&(&1.name == destroy_action.name)
|
||||
)}
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp update_destroy_actions({:error, error}), do: {:error, error}
|
||||
|
||||
defp add_base_filter({:ok, dsl_state}) do
|
||||
case Transformer.get_option(dsl_state, [:resource], :base_filter) do
|
||||
nil ->
|
||||
{:ok, Transformer.set_option(dsl_state, [:resource], :base_filter, is_nil: :archived_at)}
|
||||
|
||||
value ->
|
||||
{:ok,
|
||||
Transformer.set_option(dsl_state, [:resource], :base_filter,
|
||||
and: [[is_nil: :archived_at], value]
|
||||
)}
|
||||
end
|
||||
end
|
||||
|
||||
defp add_base_filter({:error, error}) do
|
||||
{:error, error}
|
||||
end
|
||||
|
||||
defp add_base_filter_sql({:ok, dsl_state}) do
|
||||
case Transformer.get_option(dsl_state, [:postgres], :base_filter_sql) do
|
||||
nil ->
|
||||
{:ok,
|
||||
Transformer.set_option(dsl_state, [:postgres], :base_filter_sql, "archived_at IS NULL")}
|
||||
|
||||
value ->
|
||||
{:ok,
|
||||
Transformer.set_option(
|
||||
dsl_state,
|
||||
[:postgres],
|
||||
:base_filter_sql,
|
||||
"archived_at IS NULL and (#{value})"
|
||||
)}
|
||||
end
|
||||
end
|
||||
|
||||
defp add_base_filter_sql({:error, error}) do
|
||||
{:error, error}
|
||||
end
|
||||
end
|
79
mix.exs
Normal file
79
mix.exs
Normal file
|
@ -0,0 +1,79 @@
|
|||
defmodule AshArchival.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
@version "0.1.0"
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :ash_archival,
|
||||
version: @version,
|
||||
elixir: "~> 1.13",
|
||||
source_url: "https://github.com/ash-project/ash_archival",
|
||||
homepage_url: "https://github.com/ash-project/ash_archival",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
aliases: aliases(),
|
||||
package: package(),
|
||||
deps: deps(),
|
||||
docs: docs(),
|
||||
consolidate_protocols: Mix.env() != :test
|
||||
]
|
||||
end
|
||||
|
||||
defp package do
|
||||
[
|
||||
name: :ash_postgres,
|
||||
licenses: ["MIT"],
|
||||
links: %{
|
||||
GitHub: "https://github.com/ash-project/ash_archival"
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
defp docs do
|
||||
[
|
||||
main: "readme",
|
||||
source_ref: "v#{@version}"
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help compile.app" to learn about applications.
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
# {:ash, ash_version("~> 1.52.0-rc.17")},
|
||||
{:ash, path: "../../ash/ash"},
|
||||
{:git_ops, "~> 2.4.5", only: :dev},
|
||||
{:ex_doc, "~> 0.22", only: :dev, runtime: false},
|
||||
{:ex_check, "~> 0.14", only: :dev},
|
||||
{:credo, ">= 0.0.0", only: :dev, runtime: false},
|
||||
{:dialyxir, ">= 0.0.0", only: :dev, runtime: false},
|
||||
{:sobelow, ">= 0.0.0", only: :dev, runtime: false},
|
||||
{:excoveralls, "~> 0.14", only: [:dev, :test]},
|
||||
{:elixir_sense, github: "elixir-lsp/elixir_sense", only: [:dev, :test]}
|
||||
]
|
||||
end
|
||||
|
||||
defp aliases do
|
||||
[
|
||||
sobelow:
|
||||
"sobelow --skip -i Config.Secrets --ignore-files lib/migration_generator/migration_generator.ex",
|
||||
credo: "credo --strict",
|
||||
"ash.formatter": "ash.formatter --extensions AshArchival.Resource"
|
||||
]
|
||||
end
|
||||
|
||||
defp ash_version(default_version) do
|
||||
case System.get_env("ASH_VERSION") do
|
||||
nil -> default_version
|
||||
"local" -> [path: "../ash"]
|
||||
"main" -> [git: "https://github.com/ash-project/ash.git"]
|
||||
version -> "~> #{version}"
|
||||
end
|
||||
end
|
||||
end
|
47
mix.lock
Normal file
47
mix.lock
Normal file
|
@ -0,0 +1,47 @@
|
|||
%{
|
||||
"ash": {:hex, :ash, "1.52.0-rc.17", "df6c355a284171611cf009e251079c9c43caf043d78482eaeeeb4a0f51330e64", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: true]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8.0", [hex: :ets, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.3.5", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.9", [hex: :sourceror, repo: "hexpm", optional: false]}, {:stream_data, "~> 0.5.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:timex, ">= 3.0.0", [hex: :timex, repo: "hexpm", optional: false]}], "hexpm", "93dbf26c5c69659f6fd9cd126a659f9a91b9b3f1ca77c9294e403fdfe1d11270"},
|
||||
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
|
||||
"certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
|
||||
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
||||
"comparable": {:hex, :comparable, "1.0.0", "bb669e91cedd14ae9937053e5bcbc3c52bb2f22422611f43b6e38367d94a495f", [:mix], [{:typable, "~> 0.1", [hex: :typable, repo: "hexpm", optional: false]}], "hexpm", "277c11eeb1cd726e7cd41c6c199e7e52fa16ee6830b45ad4cdc62e51f62eb60c"},
|
||||
"credo": {:hex, :credo, "1.6.4", "ddd474afb6e8c240313f3a7b0d025cc3213f0d171879429bf8535d7021d9ad78", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "c28f910b61e1ff829bffa056ef7293a8db50e87f2c57a9b5c3f57eee124536b7"},
|
||||
"decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"},
|
||||
"dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"},
|
||||
"docsh": {:hex, :docsh, "0.7.2", "f893d5317a0e14269dd7fe79cf95fb6b9ba23513da0480ec6e77c73221cae4f2", [:rebar3], [{:providers, "1.8.1", [hex: :providers, repo: "hexpm", optional: false]}], "hexpm", "4e7db461bb07540d2bc3d366b8513f0197712d0495bb85744f367d3815076134"},
|
||||
"earmark_parser": {:hex, :earmark_parser, "1.4.26", "f4291134583f373c7d8755566122908eb9662df4c4b63caa66a0eabe06569b0a", [:mix], [], "hexpm", "48d460899f8a0c52c5470676611c01f64f3337bad0b26ddab43648428d94aabc"},
|
||||
"ecto": {:hex, :ecto, "3.8.4", "e06b8b87e62b27fea17fd2ff6041572ddd10339fd16cdf58446e402c6c90a74b", [:mix], [{:decimal, "~> 1.6 or ~> 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", "f9244288b8d42db40515463a008cf3f4e0e564bb9c249fe87bf28a6d79fe82d4"},
|
||||
"elixir_make": {:hex, :elixir_make, "0.6.3", "bc07d53221216838d79e03a8019d0839786703129599e9619f4ab74c8c096eac", [:mix], [], "hexpm", "f5cbd651c5678bcaabdbb7857658ee106b12509cd976c2c2fca99688e1daf716"},
|
||||
"elixir_sense": {:git, "https://github.com/elixir-lsp/elixir_sense.git", "e224e6d8e432ae3e307c47a86247e7959f943759", []},
|
||||
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
|
||||
"ets": {:hex, :ets, "0.8.1", "8ff9bcda5682b98493f8878fc9dbd990e48d566cba8cce59f7c2a78130da29ea", [:mix], [], "hexpm", "6be41b50adb5bc5c43626f25ea2d0af1f4a242fb3fad8d53f0c67c20b78915cc"},
|
||||
"ex_check": {:hex, :ex_check, "0.14.0", "d6fbe0bcc51cf38fea276f5bc2af0c9ae0a2bb059f602f8de88709421dae4f0e", [:mix], [], "hexpm", "8a602e98c66e6a4be3a639321f1f545292042f290f91fa942a285888c6868af0"},
|
||||
"ex_doc": {:hex, :ex_doc, "0.28.4", "001a0ea6beac2f810f1abc3dbf4b123e9593eaa5f00dd13ded024eae7c523298", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "bf85d003dd34911d89c8ddb8bda1a958af3471a274a4c2150a9c01c78ac3f8ed"},
|
||||
"excoveralls": {:hex, :excoveralls, "0.14.5", "5c685449596e962c779adc8f4fb0b4de3a5b291c6121097572a3aa5400c386d3", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e9b4a9bf10e9a6e48b94159e13b4b8a1b05400f17ac16cc363ed8734f26e1f4e"},
|
||||
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
|
||||
"getopt": {:hex, :getopt, "1.0.1", "c73a9fa687b217f2ff79f68a3b637711bb1936e712b521d8ce466b29cbf7808a", [:rebar3], [], "hexpm", "53e1ab83b9ceb65c9672d3e7a35b8092e9bdc9b3ee80721471a161c10c59959c"},
|
||||
"gettext": {:hex, :gettext, "0.19.1", "564953fd21f29358e68b91634799d9d26989f8d039d7512622efb3c3b1c97892", [:mix], [], "hexpm", "10c656c0912b8299adba9b061c06947511e3f109ab0d18b44a866a4498e77222"},
|
||||
"git_cli": {:hex, :git_cli, "0.3.0", "a5422f9b95c99483385b976f5d43f7e8233283a47cda13533d7c16131cb14df5", [:mix], [], "hexpm", "78cb952f4c86a41f4d3511f1d3ecb28edb268e3a7df278de2faa1bd4672eaf9b"},
|
||||
"git_ops": {:hex, :git_ops, "2.4.5", "185a724dfde3745edd22f7571d59c47a835cf54ded67e9ccbc951920b7eec4c2", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e323a5b01ad53bc8c19c3a444be3e61ed7803ecd2e95530446ae9327d0143ecc"},
|
||||
"hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~>2.9.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.3.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", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"},
|
||||
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
|
||||
"jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"},
|
||||
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
|
||||
"makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"},
|
||||
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
|
||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
|
||||
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
|
||||
"nimble_options": {:hex, :nimble_options, "0.3.7", "1e52dd7673d36138b1a5dede183b5d86dff175dc46d104a8e98e396b85b04670", [:mix], [], "hexpm", "2086907e6665c6b6579be54ef5001928df5231f355f71ed258f80a55e9f63633"},
|
||||
"nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"},
|
||||
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
|
||||
"picosat_elixir": {:hex, :picosat_elixir, "0.2.1", "407dcb90755167fd9e3311b60565ff32ed0d234010363406c07cdb4175b95bc5", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "68f4bdb2ac3b594209e54625d3d58c9e2e98b90f2ec8e03235f66e88c9eda5fe"},
|
||||
"providers": {:hex, :providers, "1.8.1", "70b4197869514344a8a60e2b2a4ef41ca03def43cfb1712ecf076a0f3c62f083", [:rebar3], [{:getopt, "1.0.1", [hex: :getopt, repo: "hexpm", optional: false]}], "hexpm", "e45745ade9c476a9a469ea0840e418ab19360dc44f01a233304e118a44486ba0"},
|
||||
"sobelow": {:hex, :sobelow, "0.11.1", "23438964486f8112b41e743bbfd402da3e5b296fdc9eacab29914b79c48916dd", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "9897363a7eff96f4809304a90aad819e2ad5e5d24db547af502885146746a53c"},
|
||||
"sourceror": {:hex, :sourceror, "0.11.1", "1b80efe84330beefb6b3da95b75c1e1cdefe9dc785bf4c5064fae251a8af615c", [:mix], [], "hexpm", "22b6828ee5572f6cec75cc6357f3ca6c730a02954cef0302c428b3dba31e5e74"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
|
||||
"stream_data": {:hex, :stream_data, "0.5.0", "b27641e58941685c75b353577dc602c9d2c12292dd84babf506c2033cd97893e", [:mix], [], "hexpm", "012bd2eec069ada4db3411f9115ccafa38540a3c78c4c0349f151fc761b9e271"},
|
||||
"telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"},
|
||||
"timex": {:hex, :timex, "3.7.8", "0e6e8bf7c0aba95f1e13204889b2446e7a5297b1c8e408f15ab58b2c8dc85f81", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8f3b8edc5faab5205d69e5255a1d64a83b190bab7f16baa78aefcb897cf81435"},
|
||||
"typable": {:hex, :typable, "0.3.0", "0431e121d124cd26f312123e313d2689b9a5322b15add65d424c07779eaa3ca1", [:mix], [], "hexpm", "880a0797752da1a4c508ac48f94711e04c86156f498065a83d160eef945858f8"},
|
||||
"tzdata": {:hex, :tzdata, "1.1.1", "20c8043476dfda8504952d00adac41c6eda23912278add38edc140ae0c5bcc46", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a69cec8352eafcd2e198dea28a34113b60fdc6cb57eb5ad65c10292a6ba89787"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
|
||||
}
|
153
test/archival_test.exs
Normal file
153
test/archival_test.exs
Normal file
|
@ -0,0 +1,153 @@
|
|||
defmodule ArchivalTest do
|
||||
use ExUnit.Case
|
||||
|
||||
defmodule Post do
|
||||
use Ash.Resource,
|
||||
data_layer: Ash.DataLayer.Ets,
|
||||
extensions: [AshArchival.Resource]
|
||||
|
||||
ets do
|
||||
table(:posts)
|
||||
private?(true)
|
||||
end
|
||||
|
||||
archive do
|
||||
archive_related([:comments])
|
||||
end
|
||||
|
||||
actions do
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key(:id)
|
||||
end
|
||||
|
||||
relationships do
|
||||
has_many(:comments, ArchivalTest.Comment)
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Comment do
|
||||
use Ash.Resource,
|
||||
data_layer: Ash.DataLayer.Ets,
|
||||
extensions: [AshArchival.Resource]
|
||||
|
||||
ets do
|
||||
table(:comments)
|
||||
private?(true)
|
||||
end
|
||||
|
||||
actions do
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key(:id)
|
||||
end
|
||||
|
||||
relationships do
|
||||
belongs_to :post, Post do
|
||||
attribute_writable?(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmodule CommentWithArchive do
|
||||
use Ash.Resource,
|
||||
data_layer: Ash.DataLayer.Ets
|
||||
|
||||
ets do
|
||||
table(:comments)
|
||||
private?(true)
|
||||
end
|
||||
|
||||
actions do
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key(:id)
|
||||
attribute(:archived_at, :utc_datetime_usec)
|
||||
end
|
||||
|
||||
relationships do
|
||||
belongs_to(:post, Post)
|
||||
end
|
||||
end
|
||||
|
||||
defmodule PostWithArchive do
|
||||
use Ash.Resource,
|
||||
data_layer: Ash.DataLayer.Ets
|
||||
|
||||
ets do
|
||||
private?(true)
|
||||
table(:posts)
|
||||
end
|
||||
|
||||
actions do
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
attributes do
|
||||
uuid_primary_key(:id)
|
||||
attribute(:archived_at, :utc_datetime_usec)
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Registry do
|
||||
use Ash.Registry
|
||||
|
||||
entries do
|
||||
entry(Post)
|
||||
entry(PostWithArchive)
|
||||
entry(Comment)
|
||||
entry(CommentWithArchive)
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Api do
|
||||
use Ash.Api
|
||||
|
||||
resources do
|
||||
registry(Registry)
|
||||
end
|
||||
end
|
||||
|
||||
test "destroying a record archives it" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.for_create(:create)
|
||||
|> Api.create!()
|
||||
|
||||
assert :ok =
|
||||
post
|
||||
|> Api.destroy!()
|
||||
|
||||
[archived] = Api.read!(PostWithArchive)
|
||||
|
||||
assert archived.id == post.id
|
||||
assert archived.archived_at
|
||||
end
|
||||
|
||||
test "destroying a record archives any `archive_related` it has configured" do
|
||||
post =
|
||||
Post
|
||||
|> Ash.Changeset.for_create(:create)
|
||||
|> Api.create!()
|
||||
|
||||
comment =
|
||||
Comment
|
||||
|> Ash.Changeset.for_create(:create, %{post_id: post.id})
|
||||
|> Api.create!()
|
||||
|
||||
assert :ok =
|
||||
post
|
||||
|> Api.destroy!()
|
||||
|
||||
[archived] = Api.read!(CommentWithArchive)
|
||||
|
||||
assert archived.id == comment.id
|
||||
assert archived.archived_at
|
||||
end
|
||||
end
|
1
test/test_helper.exs
Normal file
1
test/test_helper.exs
Normal file
|
@ -0,0 +1 @@
|
|||
ExUnit.start()
|
Loading…
Reference in a new issue