smpallen99 / whatwasit Goto Github PK
View Code? Open in Web Editor NEWTrack changes to your Ecto models
License: MIT License
Track changes to your Ecto models
License: MIT License
Create creates for the purpose of complete auditing. Make it configurable.
No need to have copy of other data.
When I fetch version I want to see what was changed, but got all properties from object with old values.
Isn't the concept of a changeset that it's in preparation for a change? IE, you create the changeset then later you apply it.. it seems by suggesting users add prepare_version
to their models changeset method that you're now making calling changeset
have side-effects (writing to the DB), where-as before (intentionally) it had none.
Could be I'm misunderstanding the concept, but I don't think so. I feel like we need yet another function to encapsulate the concept of versioning without piggy-backing on changeset itself.
In migration we can easily add if statement for app config.
I know that calculating changeset_fields
and schema_fields
on each request is not a good idea.
Is saving them in configuration (or :ets) after app start (and before listen for requests) and read them in model is bad idea?
Now we have: version.ex
and version_map.ex
templates.
They are similar and could be written at once with some if's (based on app config).
What do you think about it?
TODO:
These methods can be useful:
{:ok, version}
or raise Whatwasit.NoUpdateVersionError
Enum
)revert_by_whodoneit([whodoneit])
For 3rd and 4th point:
revert all changes made by User
(s)
returns tuple: {:ok, versions}
or {:error, whodoneit}
or raise Whatwasit.NoUpdateVersionError
where: whodoneit
is User
Enum
that made changes after one or more changes made by selected whodoneit(s).
Optionally: force parameter that not return error tuple and delete all versions (from selected whodoneit(s) first update version).
This can be done in optional module:
defmodule MyProject.Post do
use Whatwasit
use Whatwasit.Revert
# ...
end
Maybe optionally add is_deleted
field for Version
(to preview admin activity)? What do you think about it?
whatwasit.install creates an association for the user model. This does not work if you don't have a user model and don't want to track changes.
The addition of a user model should be an option in the installer, with the default to not create the user association
If log file path was specified in configuration (for all tracked changes - dynamic, umbrella apps etc).
Example:
On %{date} %{what} with id %{id} was %{action} by '%{whodoneit_name}' (id: %{whodoneit_id}).
where:
date
: when
what
: for example article
id
: for example Article
id
action
: one of: ["create", "destroy", "update"]
whodoneit_name
: for example John
whodoneit_id
: for example John
's id
Optionally (in options):
log_formatter_module
(if not specified use default formatter).
defmodule MyProject.LogFormatter
def format(what_was_it) do
# returns: {:ok, result} tuple where result is log string
end
end
Why? For example:
whodoneit_id
- same ip and same author/user) from which created too more entries (spam bot using REST API).John
(grep by regex where for example whodoneit_id
is 5). Here I mean server admin/user, not project web admin or project developer.I want to easily generate diff for %MyProject.User
(admin and/or editor role) to preview multi line String
e.g %MyProject.Post
body
field.
This should be method that accept type
argument (Atom
or String
) and optionally lines
(Integer
).
Lines argument is to limit diff response for X lines around change (not need when type is json).
Type argument may be:
patch
command)Format for normal:
We have Enum of lines. Each line have String
(s) and/or Tuple
(s). String
(s) are not affected text. Tuple
is info about affected text, where:
["delete", "insert"]
When side-by-side we have tuple: {left_column, right_column}
where each column is array of lines (see format for nomal).
For example:
[ ["same text 1", {"insert", "text inserted in first line"}, "same text 2"], ["same text for second line ..."] ]
JSON API:
Returns json with two fields:
source
- original text
changes
- array of changes where each element has type
, line
, column
, change_length
.
For example:
{"source": "First line\nSecondd line\nThird line", "changes":[{"change_length": 1, "column": 7, "line": 2, "type": "delete"}]}
API says: "remove 7th character of 2nd column for given source".
Generating diff should be available for one and all versions (Version
) for same model and model id.
Diff support can be optional when installing.
When use whatwasit
with phoenix framework, i use mix phoenix.gen.model Post posts title:string body:string
generate a model that include a local changeset
function which conflicts with <PROJECT_NAME>.Whatwasit.Version
model. I can rename my post model changeset function to other name, but every time i created a model, i need to do repeat rename operations.
So, i suggest rename changset function name in <PROJECT_NAME>.Whatwasit.Version
model.
Compiling 1 file (.ex)
== Compilation error on file web/models/post.ex ==
** (CompileError) web/models/post.ex:1: imported WhatwasitExample.Whatwasit.Version.changeset/2 conflicts with local function
(elixir) src/elixir_locals.erl:138: :elixir_locals."-ensure_no_import_conflict/4-lc$^0/1-0-"/3
(elixir) src/elixir_locals.erl:137: anonymous fn/4 in :elixir_locals.ensure_no_import_conflict/4
(stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
(elixir) lib/kernel/parallel_compiler.ex:116: anonymous fn/4 in Kernel.ParallelCompiler.spawn_compilers/1
I want to save more data that is not stored in User model e.g request data.
For example:
Whatwasit.Repo calls always module.get_whodoneit_name_and_id/1
in insert_version/3
.
With --whodoneit-map
this doesn't work, since that code is not generated and can not return sensible values anyway.
Hi @smpallen99, @gmile, @mebezac , @lpil
In my project Whatwasit was configured and was working fine. But recently we have upgraded the elixir version to 1.13.4 and OTP-23 and while running the Exunit tests most of the tests are failing because of prepare_version(opts).
As we have not done any code change related to this and application is working as expected, but facing issues for the Exunit tests. Below is the error we are getting.
** (FunctionClauseError) no function clause matching in Ecto.Changeset.cast/4
when attempting to create or update an object.
The following arguments were given to Ecto.Changeset.cast/4:
# 1
MyTest.Whatwasit.Version
# 2
%{action: "update", item_id: 730, item_type: "AdminUser", item_user_id: nil, object: %{admin_author_id: nil, availability_flag: false, email: "[email protected]", failed_log_in_attempts: 0, full_name: "Petra fhghgf", groups: nil, id: 730, inserted_at: ~N[2023-06-09 10:46:47.691601], last_locked_at: nil, last_log_in_at: nil, last_log_in_remote_ip: nil, last_password_reset_at: nil, last_seen_at: nil, log_in_count: 0, reset_password_sent_at: nil, reset_password_token: nil, roles: ["admin"], updated_at: ~N[2023-06-09 10:46:47.692937], uuid: "df6789e9-f5a0-9876-80c4-0bgb72d777d2"}, whodoneit_admin_id: 729, whodoneit_admin_name: "test acc", whodoneit_id: nil, whodoneit_name: nil}
# 3
[:item_type, :item_id, :item_user_id, :object, :action, :whodoneit_id, :whodoneit_name, :whodoneit_admin_id, :whodoneit_admin_name]
# 4
[]
Attempted function clauses (showing 5 out of 5):
def cast(_data, %{__struct__: _} = params, _permitted, _opts)
def cast({data, types}, params, permitted, opts) when is_map(data)
def cast(%Ecto.Changeset{types: nil}, _params, _permitted, _opts)
def cast(%Ecto.Changeset{changes: changes, data: data, types: types, empty_values: empty_values} =
changeset, params, permitted, opts)
def cast(%{__struct__: module} = data, params, permitted, opts)
code: patch(
stacktrace:
(ecto 3.3.4) Ecto.Changeset.cast/4
(my_test 4.0.0) lib/my_test/shared/whatwasit_version.ex:42: MyTest.Whatwasit.Version.module_changeset/2
(my_test 4.0.0) lib/my_test/shared/whatwasit_version.ex:117: MyTest.Whatwasit.Version.insert_version/3
If I comment prepare_version(opts) in the Changeset, tests are passing.
Kindly help fixing this issue.
Thank you.
For a project that uses an LDAP user model, we should be able to support this. The LDAP id is a big int.
To support this, we can provide an install option to define the type type of the whodoneit field. As well, we can provide an option to set :name_field config item to a {Module, Fun} value. When set to a tuple, the function will be called to get the whodoneit_name value.
mix whatwasit.install
running above command throwing below error
* creating priv/repo/migrations/20180219080831_create_whatwasit_version.exs
** (UndefinedFunctionError) function Mix.Phoenix.copy_from/5 is undefined or private. Did you mean one of:
* copy_from/4
(phoenix) Mix.Phoenix.copy_from([".", :whatwasit], "priv/templates/whatwasit.install/models/whatwasit", "", [base: "AppName", alias: "AppName", human: "AppName", web_module: "AppNameWeb", module: "AppName.AppName", scoped: "AppName", singular: "appName", path: "appName", schema_fields: "", changeset_fields: "~w(item_type item_id object action)a"], [{:eex, "version.ex", "web/models/whatwasit/version.ex"}])
lib/mix/tasks/whatwasit.install.ex:122: Mix.Tasks.Whatwasit.Install.gen_version_model/1
lib/mix/tasks/whatwasit.install.ex:84: Mix.Tasks.Whatwasit.Install.do_run/1
(mix) lib/mix/task.ex:314: Mix.Task.run_task/3
(mix) lib/mix/cli.ex:80: Mix.CLI.run_task/2
(elixir) lib/code.ex:677: Code.require_file/2
basically (phoenix) Mix.Phoenix.copy_from
takes only 4 args and 5 are sent from here.
kindly help fixing this.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.