Ithibati.Migration (Ithibati v0.1.0)

Copy Markdown View Source

Creates Ithibati's tables, as code rather than as a file to copy.

You write an ordinary migration of your own and call this from it. Getting started carries the template and the configuration it reads. What follows is what the two functions take.

Options

  • :version — the schema version to build. Required. An unpinned call builds a different set of tables depending on when it runs, and a rollback that undoes neither.
  • :from — the version already present, exclusive. Defaults to 0, a database with none of these tables. A release that adds to the schema reaches an application as a second migration of its own saying where it starts. Ecto records what has already been applied in that application's own schema_migrations.

Table names are deliberately not options. The migration reads them from the schemas that go on to query these tables: Ithibati's own, and the account schema that config :ithibati, user_schema: names. The two therefore cannot disagree. An argument could have built x_sessions while Ithibati.Session went on looking for ithibati_sessions, and nothing would have failed at the time the mistake was made.

The foreign key's type is not an option either. config :ithibati, users_key_type: is compiled into the schemas, and this migration reads it back out of one of them. An application can still configure a type its own account table does not have, so the migration asks the column these foreign keys will point at what it is before it builds anything, and refuses a disagreement rather than half-applying it.

Summary

Functions

The newest schema version this release knows.

Removes what the matching up/1 built.

The columns an invitation table has to carry, for the migration that creates it.

The unique index on an invitation table's token_hash, for a migration of your own.

Builds Ithibati's tables. See the module documentation for options.

Functions

current_version()

The newest schema version this release knows.

down(opts)

Removes what the matching up/1 built.

invitation_columns(opts)

The columns an invitation table has to carry, for the migration that creates it.

This is the counterpart to Ithibati.Schema.Invitation.ithibati_invitation/0. That macro declares the fields, and this one adds the columns behind them. Both read the identifier off the schema you configured, so the two cannot name different things. That is the mistake this replaces: a token_hash written :string instead of :binary migrates without complaint and fails at the first invitation.

Call it inside a create table/2 of your own:

create table(:invitations) do
  Ithibati.Migration.invitation_columns(version: 1)

  add :role, Ecto.Enum, values: [:admin, :author]
  timestamps(type: :utc_datetime_usec)
end

It adds exactly four columns, and this list is the whole of it:

  • the identifier your invitation schema declares, :string, null: false
  • :token_hash, :binary, null: false
  • :expires_at, :utc_datetime_usec, null: false
  • :accepted_at, :utc_datetime_usec, nullable. NULL is what "not accepted yet" means, and Ithibati.Identity.Invitations.fetch/1 reads it that way

The virtual :token the schema declares is not among them, because Ithibati never stores the secret.

version: is required, for the reason up/1 gives. A migration is a record of what was built, and an unpinned call expands against whichever release is installed the next time somebody sets up a database from scratch.

Nothing here is compulsory. up/1 checks an invitation table that already exists, or one you would rather write out, either way.

invitation_index(opts)

The unique index on an invitation table's token_hash, for a migration of your own.

up/1 creates this index as part of its own run, so an application that configured invitation_schema: before migrating never needs this function. It exists for the case where you turn invitations on afterwards: up/1 has been recorded as applied and will not run again, and the token in an invitation link is a bearer secret looked up by that digest.

defmodule MyApp.Repo.Migrations.AddInvitations do
  use Ecto.Migration

  def change do
    create table(:invitations) do
      Ithibati.Migration.invitation_columns(version: 1)

      timestamps(type: :utc_datetime_usec)
    end

    Ithibati.Migration.invitation_index(version: 1)
  end
end

It is safe to call either way. It creates the index only if it is not already there, and up/1 does the same, so the two cannot collide however they are ordered.

up(opts)

Builds Ithibati's tables. See the module documentation for options.