dwyl / phoenix-ecto-append-only-log-example

📝 A step-by-step example/tutorial showing how to build a Phoenix (Elixir) App where all data is immutable (append only). Precursor to Blockchain, IPFS or Solid!
GNU General Public License v2.0
78 stars 7 forks source link

Create a UI to allow people to interact with the example? #17

Open nelsonic opened 5 years ago

nelsonic commented 5 years ago

Reading the example/tutorial code is nice, 👍 but I feel that having an example app on Heroku would be much more beginner-friendly. 🤔 Who wants to make this happen? :-)

Example: https://github.com/dwyl/phoenix-chat-example

Todo

nelsonic commented 5 years ago

The reason I think that this example is highly relevant and useful both to us (@dwyl) and to anyone else wanting to understand both the "Why?" and "How?" of append-only (accountable) application architecture is that having a simple example without any dependencies makes it clear how everything works.

I'm going to make a stab at the todo list above. I will log my progress as I go and create a PR once there is something to review.

Edit: To be clear: the reason I think this is worth doing first is to understand the "alog" problem from "first principals" before attempting to generalise the solution in github.com/dwyl/alog Hopefully others will follow my logic here. 🌈

nelsonic commented 5 years ago

Going to try and use mix phx.gen.html command to generate the HTML (form) for the address book: https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Html.html

Take the mix phx.gen.schema Address command as the basis and adapt it for gen.html

mix phx.gen.schema Address addresses name:string address_line_1:string address_line_2:string city:string postcode:string tel:string

Gen HTML:

mix phx.gen.html Address addresses name:string address_line_1:string address_line_2:string city:string postcode:string tel:string

Got the following error:

** (Mix) Expected the schema, "addresses", to be a valid module name

mix phx.gen.html, phx.gen.json and phx.gen.context expect a
context module name, followed by singular and plural names of
the generated resource, ending with any number of attributes.
For example:

    mix phx.gen.html Accounts User users name:string
    mix phx.gen.json Accounts User users name:string
    mix phx.gen.context Accounts User users name:string

The context serves as the API boundary for the given resource.
Multiple resources may belong to a context and a resource may be
split over distinct contexts (such as Accounts.User and Payments.User).

In my own "throw away" example: https://github.com/nelsonic/append-only-log-ex I'm going to try deleting the schema and re-creating it using the gen.html command:

mix ecto.drop && MIX_ENV=test mix ecto.drop

You should expect to see:

The database for Append.Repo has been dropped
The database for Append.Repo has been dropped

Try running the Gen HTML command again:

mix phx.gen.html Address addresses name:string address_line_1:string address_line_2:string city:string postcode:string tel:string

That failed because the address.ex file already exists. Deleting the file: /append-only-log-ex/lib/append/address.ex Also deleting the migrations: image

After deleting the files, let's attempt to run the gen.html command again:

mix phx.gen.html Address addresses name:string address_line_1:string address_line_2:string city:string postcode:string tel:string

Derp, we need to add the context! Error message:

** (Mix) Expected the schema, "addresses", to be a valid module name

mix phx.gen.html, phx.gen.json and phx.gen.context expect a
context module name, followed by singular and plural names of
the generated resource, ending with any number of attributes.
For example:

    mix phx.gen.html Accounts User users name:string
    mix phx.gen.json Accounts User users name:string
    mix phx.gen.context Accounts User users name:string

The context serves as the API boundary for the given resource.
Multiple resources may belong to a context and a resource may be
split over distinct contexts (such as Accounts.User and Payments.User).

Add the "Accounts" context to the the gen.html command and run it again:

mix phx.gen.html Accounts Address addresses name:string address_line_1:string address_line_2:string city:string postcode:string tel:string

This time it worked: 🎉

* creating lib/append_web/controllers/address_controller.ex
* creating lib/append_web/templates/address/edit.html.eex
* creating lib/append_web/templates/address/form.html.eex
* creating lib/append_web/templates/address/index.html.eex
* creating lib/append_web/templates/address/new.html.eex
* creating lib/append_web/templates/address/show.html.eex
* creating lib/append_web/views/address_view.ex
* creating test/append_web/controllers/address_controller_test.exs
* creating lib/append/accounts/address.ex
* creating priv/repo/migrations/20190425093946_create_addresses.exs
* creating lib/append/accounts.ex
* injecting lib/append/accounts.ex
* creating test/append/accounts/accounts_test.exs
* injecting test/append/accounts/accounts_test.exs

Add the resource to your browser scope in lib/append_web/router.ex:

    resources "/addresses", AddressController

Remember to update your repository by running migrations:

    $ mix ecto.migrate
nelsonic commented 5 years ago

Going to follow the instructions given after running the gen.html command: open the lib/append_web/router.ex file and add

    resources "/addresses", AddressController

Update your repository by running migrations:

$ mix ecto.migrate

Fails:

[error] Postgrex.Protocol (#PID<0.301.0>) failed to connect: ** (Postgrex.Error) FATAL 3D000 (invalid_catalog_name) database "append_dev" does not exist
[error] Postgrex.Protocol (#PID<0.300.0>) failed to connect: ** (Postgrex.Error) FATAL 3D000 (invalid_catalog_name) database "append_dev" does not exist
[error] Postgrex.Protocol (#PID<0.301.0>) failed to connect: ** (Postgrex.Error) FATAL 3D000 (invalid_catalog_name) database "append_dev" does not exist
[error] Could not create schema migrations table. This error usually happens due to the following:

  * The database does not exist
  * The "schema_migrations" table, which Ecto uses for managing
    migrations, was defined by another library

To fix the first issue, run "mix ecto.create".

To address the second, you can run "mix ecto.drop" followed by
"mix ecto.create". Alternatively you may configure Ecto to use
another table for managing migrations:

    config :append, Append.Repo,
      migration_source: "some_other_table_for_schema_migrations"

The full error report is shown below.

Run the drop command:

mix ecto.drop && MIX_ENV=test mix ecto.drop

Then run the migrate command again:

mix ecto.create
mix ecto.migrate

Output: (success)

[info] == Running 20190425093946 Append.Repo.Migrations.CreateAddresses.change/0 forward
[info] create table addresses
[info] == Migrated 20190425093946 in 0.0s

Run the mix phx.server command to start the server:

mix phx.server

Visit the app in the browser: http://localhost:4000/addresses image

nelsonic commented 5 years ago

With the addition of the Accounts context the address.ex schema is now in: /lib/append/accounts/address.ex so that's where we need to make our timestamp change in step 2 of the tutorial.

Now we can run the migrate:

mix ecto.migrate

Output:

Compiling 1 file (.ex)
[info] == Running 20190425093946 Append.Repo.Migrations.CreateAddresses.change/0 forward
[info] create table addresses
[info] execute "REVOKE UPDATE, DELETE ON TABLE addresses FROM append_only"
[info] == Migrated 20190425093946 in 0.0s
nelsonic commented 5 years ago

Run the app mix phx.server Visit http://localhost:4000/addresses/new image

Totes works! image

Now attempt to edit the address: http://localhost:4000/addresses/1/edit image

Fails because we revoked the UPDATE privileges in our migration (above): image

nelsonic commented 5 years ago

The question we have to ask ourselves now is: are we going to use the default phoenix auto-incrementing id (in the above case 1) for the id for records. That will always result in errors.

nelsonic commented 5 years ago

in the /test/append/address_test.exs we need to change the line:

alias Append.Address

To:

alias Append.Accounts.Address
nelsonic commented 5 years ago

No changes were required to the lib/append/append_only_log.ex section of the existing tutorial we just had to open the file /lib/append/accounts/address.ex add the line:

  use Append.AppendOnlyLog #include the functions from this module's '__using__' macro.

First test passes: image

In the tutorial, remember to change all instances of Append.Address to Append.Accounts.Address

Remember to link to the issue discussing merits of macros: https://github.com/dwyl/phoenix-ecto-append-only-log-example/issues/8

nelsonic commented 5 years ago

Minor detour (SPIKE) to investigate storing history of a record: https://github.com/dwyl/ecto-postgres-pubsub-spike/issues/1