Giter Site home page Giter Site logo

acme_bank's People

Contributors

bchase avatar gusaiani avatar wojtekmach avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

acme_bank's Issues

Question about the Bank OTP app

I have a question about the Bank OTP app. Is my understanding correct that it is a higher level app that controls how the other lower level (Auth etc) interact? Also, would it be possible for there to be multiple higher level apps in order to split up the business logic or is there generally just one?

Hope this makes sense. Thanks so much :)

Testing issue with CustomerRegistration

Thanks for creating this as an example of doing an Umbrella project. It's been very helpful as a resource. :)

The problem I have is around transactions and tests when testing a feature like CustomerRegistration that spans Repos (Bank and Auth).

If you create a new test file called customer_registration_test.exs and add the following contents you can see the problem I'm having...

defmodule Bank.CustomerRegistrationTest do
  use Bank.Case
  # doctest Bank

  alias Bank.CustomerRegistration

  @moduletag isolation: :serializable

  describe "create/3" do
    test "creates auth and user records" do
      # Ecto.Adapters.SQL.Sandbox.mode(Repo, {:shared, self()})
      {:ok, %{update: customer}} = CustomerRegistration.create("name", "[email protected]", "asdfasdf1")
      assert customer.auth_account_id != nil
    end
  end
end

If you run mix test at the umbrella root, it fails with this error...

  1) test create/3 creates auth and user records (Bank.CustomerRegistrationTest)
     test/bank/customer_registration_test.exs:10
     ** (DBConnection.OwnershipError) cannot find ownership process for #PID<0.805.0>.

     When using ownership, you must manage connections in one
     of the three ways:

       * By explicitly checking out a connection
       * By explicitly allowing a spawned process
       * By running the pool in shared mode

     The first two options require every new process to explicitly
     check a connection out or be allowed by calling checkout or
     allow respectively.

     The third option requires a {:shared, pid} mode to be set.
     If using shared mode in tests, make sure your tests are not
     async.

     If you are reading this error, it means you have not done one
     of the steps above or that the owner process has crashed.

     See Ecto.Adapters.SQL.Sandbox docs for more information.
     stacktrace:
[...]

If I run the tests from the Bank application (cd apps/bank) it will pass the first time and fail the second.

$ mix test
................

Finished in 0.2 seconds
16 tests, 0 failures

Randomized with seed 284417

$ mix test
...............

  1) test create/3 creates auth and user records (Bank.CustomerRegistrationTest)
     test/bank/customer_registration_test.exs:10
     ** (MatchError) no match of right hand side value: {:error, :account, #Ecto.Changeset<action: :insert, changes: %{email: "[email protected]", password: "asdfasdf1", password_hash: "$2b$04$SXRoZna6IjYLIQeo1VdsFeXiadeR2hq3Sd1t5g48v2qyRtJpFekIG"}, errors: [email: {"has already been taken", []}], data: #Auth.Account<>, valid?: false>, %{customer: %Bank.Customer{__meta__: #Ecto.Schema.Metadata<:loaded, "bank_customers">, auth_account_id: nil, email: "[email protected]", id: 46, inserted_at: #Ecto.DateTime<2016-10-13 11:44:10>, updated_at: #Ecto.DateTime<2016-10-13 11:44:10>, username: "name", wallet: %Bank.Ledger.Account{__meta__: #Ecto.Schema.Metadata<:loaded, "bank_accounts">, currency: "USD", id: 103, inserted_at: #Ecto.DateTime<2016-10-13 11:44:10>, name: "Wallet: name", type: "liability", updated_at: #Ecto.DateTime<2016-10-13 11:44:10>}, wallet_id: 103}}}
     stacktrace:
       test/bank/customer_registration_test.exs:12: (test)

The problem here is that the created Auth.Account isn't cleaned up after the first test run and we have left-over data that creates a conflict. You have to reset the DB like this MIX_ENV=test mix ecto.reset.

One solution I had was to delete the Auth Repo data in the setup of the test, but then you'd have to run async: false and it felt messy.

Have you solved any of these problems yet?

Question Related To MasterProxy

Hello,

I have question related to MasterProxy.
In the code below

 cond do
      conn.request_path =~ ~r{/backoffice} ->
        Backoffice.Endpoint.call(conn, [])
      true ->
        BankWeb.Endpoint.call(conn, [])
    end

It's uses /backoffice regular expression to dispatch to two endpoints.
I think we still missing something in here. In both the endpoints, there are assets is fetched through static_plug in both the endpoints. Do you know how to edit the code to cover those cases also.

Thanks
Dev.

Heroku build error

Getting this error when trying to deploy to heroku (using the quick deploy option on the repo):

[33mwarning: [0mvariable "package" does not exist and is being expanded to "package()", please use parentheses to remove the ambiguity or change the variable name
  /app/deps/scrivener_ecto/mix.exs:10

�[33mwarning: �[0mvariable "deps" does not exist and is being expanded to "deps()", please use parentheses to remove the ambiguity or change the variable name
  /app/deps/scrivener_ecto/mix.exs:12

�[33mwarning: �[0mvariable "aliases" does not exist and is being expanded to "aliases()", please use parentheses to remove the ambiguity or change the variable name
  /app/deps/scrivener_ecto/mix.exs:13

�[33mwarning: �[0mvariable "deps" does not exist and is being expanded to "deps()", please use parentheses to remove the ambiguity or change the variable name
  /app/deps/ex_admin/mix.exs:16

�[33mwarning: �[0mvariable "package" does not exist and is being expanded to "package()", please use parentheses to remove the ambiguity or change the variable name
  /app/deps/ex_admin/mix.exs:17

�[33mwarning: �[0mvariable "package" does not exist and is being expanded to "package()", please use parentheses to remove the ambiguity or change the variable name
  /app/deps/ex_queb/mix.exs:13

�[33mwarning: �[0mvariable "deps" does not exist and is being expanded to "deps()", please use parentheses to remove the ambiguity or change the variable name
  /app/deps/ex_queb/mix.exs:15

�[31m
15:58:27.684 [error] GenServer #PID<0.255.0> terminating
** (ArgumentError) argument error
    (postgrex) lib/postgrex/utils.ex:38: anonymous fn/1 in Postgrex.Utils.parse_version/1
    (elixir) lib/enum.ex:1229: Enum."-map/2-lists^map/1-0-"/2
    (elixir) lib/enum.ex:1229: Enum."-map/2-lists^map/1-0-"/2
    (postgrex) lib/postgrex/utils.ex:38: Postgrex.Utils.parse_version/1
    (postgrex) lib/postgrex/protocol.ex:497: Postgrex.Protocol.bootstrap_send/4
    (postgrex) lib/postgrex/protocol.ex:353: Postgrex.Protocol.handshake/2
    (db_connection) lib/db_connection/connection.ex:134: DBConnection.Connection.connect/2
    (connection) lib/connection.ex:622: Connection.enter_connect/5
Last message: nil
State: Postgrex.Protocol
�[0m�[31m
15:58:27.698 [error] GenServer #PID<0.262.0> terminating
** (ArgumentError) argument error
    (postgrex) lib/postgrex/utils.ex:38: anonymous fn/1 in Postgrex.Utils.parse_version/1
    (elixir) lib/enum.ex:1229: Enum."-map/2-lists^map/1-0-"/2
    (elixir) lib/enum.ex:1229: Enum."-map/2-lists^map/1-0-"/2
    (postgrex) lib/postgrex/utils.ex:38: Postgrex.Utils.parse_version/1
    (postgrex) lib/postgrex/protocol.ex:497: Postgrex.Protocol.bootstrap_send/4
    (postgrex) lib/postgrex/protocol.ex:353: Postgrex.Protocol.handshake/2
    (db_connection) lib/db_connection/connection.ex:134: DBConnection.Connection.connect/2
    (connection) lib/connection.ex:622: Connection.enter_connect/5
Last message: nil
State: Postgrex.Protocol
�[0m** (exit) exited in: :gen_server.call(#PID<0.255.0>, {:checkout, #Reference<0.0.6.1820>, true, :infinity}, 5000)
    ** (EXIT) an exception was raised:
        ** (ArgumentError) argument error
            (postgrex) lib/postgrex/utils.ex:38: anonymous fn/1 in Postgrex.Utils.parse_version/1
            (elixir) lib/enum.ex:1229: Enum."-map/2-lists^map/1-0-"/2
            (elixir) lib/enum.ex:1229: Enum."-map/2-lists^map/1-0-"/2
            (postgrex) lib/postgrex/utils.ex:38: Postgrex.Utils.parse_version/1
            (postgrex) lib/postgrex/protocol.ex:497: Postgrex.Protocol.bootstrap_send/4
            (postgrex) lib/postgrex/protocol.ex:353: Postgrex.Protocol.handshake/2
            (db_connection) lib/db_connection/connection.ex:134: DBConnection.Connection.connect/2
            (connection) lib/connection.ex:622: Connection.enter_connect/5
    (db_connection) lib/db_connection/poolboy.ex:112: DBConnection.Poolboy.checkout/3
    (db_connection) lib/db_connection.ex:712: DBConnection.checkout/2
    (db_connection) lib/db_connection.ex:619: DBConnection.run/3
    (db_connection) lib/db_connection.ex:921: DBConnection.run_meter/3
    (db_connection) lib/db_connection.ex:463: DBConnection.prepare_execute/4
    (ecto) lib/ecto/adapters/postgres/connection.ex:91: Ecto.Adapters.Postgres.Connection.execute/4
    (ecto) lib/ecto/adapters/sql.ex:235: Ecto.Adapters.SQL.sql_call/6
    (ecto) lib/ecto/adapters/sql.ex:185: Ecto.Adapters.SQL.query!/5

npm install for submodules skipped

For correct work of this example you also need to install packages for phoenix applications:

$ mix ecto.setup
$ cd apps/bank_web
$ npm install
$ cd ../backoffice/
$ npm install
$ cd ../..
$ mix phoenix.server

SSL not available

Hi, I'm new with Elixir based apps and following your steps, the next error was shown when I tried to run the migrations with ecto.setup:

18:35:48.949 [error] Postgrex.Protocol (#PID<0.415.0>) failed to connect: ** (Postgrex.Error) ssl not available

I've tried upgrading and recompiling all the libraries, incluiding the postgrex, I removed the SSL parameter located in the Auth configuration. It is something that I`m missing?

Thank you.

Auth: link to customer account

Looking at the code I see that the user account has a "foreign key" to the authentication:

schema "bank_customers" do
  field :auth_account_id, :integer

Shouldn't this relation be inversed: auth linking to the underlying customer ?

schema "auth_accounts" do
  field :customer_id, :integer

This would bring more modularity: being able to swap/add authentication methods in the future without changing the customer model.
It's still easy to use: the session bit authenticates the user and we get the customer_id without going through an extra query (if we're only interested in the id).

I thought that was what slide 112 was all about, it enlightened me!
Though the implementation I found brought me down a bit..

Tell me what you think!

Error when trying to run two instances of the app from MasterProxy

[error] Failed to start Ranch listener MasterProxy.Plug.HTTP in :ranch_tcp:listen([port: 3333]) for reason :eaddrinuse (address already in use)

When I run mix run --no-halt and iex -S mix inside of /apps/master_proxy, I get the error above. I thought about trying to have something like this:

if :erlang.whereis(MasterProxy.Plug.HTTP) == :undefined, do: [Plug.Adapters.Cowboy.child_spec(:http, MasterProxy.Plug, [], [port: port])], else: []

Doesn't seem to work though. Any thoughts?

Question: State of balances

I'm new to OTP and Elixir.

If I'm understanding this correctly, the balance for each account is kept in memory in OTP. Each time the application launches or a supervisor needs to restart a GenServer, the balance would need to be recalculated from the ledger entries in bank_entries table in the database. When a transaction occurs, the OTP records it to the database and transforms the values in memory.

If a balance is queried through the web interface, it would go to the OTP bank and read it directly from memory instead of having to recalculate it.

Is this correct? Am I understanding this the way it should be?

This call from bank.ex

  def balance(%Customer{wallet: wallet}), do: Ledger.balance(wallet)

Is calling ledger.ex

def balance(%Account{id: id, type: type, currency: currency}) do
   q = from(t in Entry,
            select: fragment("SUM(CASE WHEN b0.type = 'credit' THEN (b0.amount).cents ELSE -(b0.amount).cents END)"),
            where: t.account_id == ^id)

   balance = Repo.one(q) || 0
   balance = do_balance(balance, type)
   %Money{cents: balance, currency: currency}
 end

and this is where it is kept in memory.

Question: Websockets and MasterProxy

Hi @wojtekmach,

Thanks again for this awesome example and talk, I am studying this and had some questions. I hope you don't mind if I open issues here.

My question is around websockets. I have a similar situation where there is both a "public" and an "admin" client that would be great fit for two different phoenix apps in an umbrella. However at least one of them needs a websocket connection as well as an API.

I believe in this example app websockets would not be supported because plug doesn't support them. Have you thought about this at all?

I am assuming you would have to use Phoenix.Endpoint in the masterproxy app (but maybe not as a Phoenix app?). Anything to consider with that?

Issue on bank_entries amount

Amount shown for the bank_entries amount does not separate decimals from the amount being transferred. Have transferred "5.00" USD, parses well, however, it appears as "500" both in bank_entries table and the transaction log.

SET TRANSACTION ISOLATION LEVEL serializable []
[debug] QUERY OK db=2.9ms
INSERT INTO "bank_entries" ("account_id","amount","description","type","inserted_at","updated_at") VALUES ($1,$2,$3,$4,$5,$6) RETURNING "id" [3, {500, "USD"}, "pay back time. !!!", "debit", {{2018, 3, 2}, {4, 46, 1, 0}}, {{2018, 3, 2}, {4, 46, 1, 0}}]
[debug] QUERY OK db=4.3ms
INSERT INTO "bank_entries" ("account_id","amount","description","type","inserted_at","updated_at") VALUES ($1,$2,$3,$4,$5,$6) RETURNING "id" [1, {500, "USD"}, "pay back time. !!!", "credit", {{2018, 3, 2}, {4, 46, 1, 0}}, {{2018, 3, 2}, {4, 46, 1, 0}}]
[debug] QUERY OK source="bank_entries" db=3.7ms

Does it something to do with multiplying with 100 in the money.ex?

defp do_parse(sign, dollars, cents, currency) do
sign = if sign == "-", do: -1, else: 1
cents = sign * (String.to_integer(dollars) * 100 + String.to_integer(cents))
{:ok, %Money{cents: cents, currency: currency}}
end

Websocket unable to connect between proxied apps and client using Master proxy

Using the boilerplate "hello" app an the Phoenix guides site and the master proxy in this application I am seeing the following errors on my websocket connections:

[info] GET /phoenix/live_reload/socket/websocket
[debug] ** (Phoenix.Router.NoRouteError) no route found for GET /phoenix/live_reload/socket/websocket (HelloWeb.Router)
    (hello) lib/hello_web/router.ex:1: HelloWeb.Router.__match_route__/4
    (hello) lib/phoenix/router.ex:303: HelloWeb.Router.call/2
    (hello) lib/hello_web/endpoint.ex:1: HelloWeb.Endpoint.plug_builder_call/2
    (hello) lib/plug/debugger.ex:99: HelloWeb.Endpoint."call (overridable 3)"/2
    (hello) lib/hello_web/endpoint.ex:1: HelloWeb.Endpoint.call/2
    (plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
    (cowboy) /media/iampeterbanjo/data/programs/elixir/portfolio/deps/cowboy/src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4

The endpoint for the hello app are boilerplate:

  plug Plug.Static,
    at: "/", from: :hello, gzip: false,
    only: ~w(css fonts images js favicon.ico robots.txt)

  # Code reloading can be explicitly enabled under the
  # :code_reloader configuration of your endpoint.
  if code_reloading? do
    socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
    plug Phoenix.LiveReloader
    plug Phoenix.CodeReloader
  end

Since there is only one app at the moment, my proxy plug.ex is:

defmodule Proxy.Plug do
  def init(options) do
    options
  end

  def call(conn, _opts) do
    cond do
      false ->
        HelloWeb.Endpoint.call(conn, [])
      true ->
        HelloWeb.Endpoint.call(conn, [])
    end
  end
end

This happens when I start the phoenix web server for the umbrella app and not the sub-app. A repo of my code is at https://github.com/iampeterbanjo/hello-proxy

Is there a way to proxy the websockets as well in the master proxy?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.