Fix api scope

Fix api scope

defmodule Phoenix.Integration.CodeGeneration.AppWithDefaultsTest do
  use Phoenix.Integration.CodeGeneratorCase, async: true

  describe "new with defaults" do
    test "has no compilation or formatter warnings" do
      with_installer_tmp("new with defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "phx_blog")

        assert_no_compilation_warnings(app_root_path)
        assert_passes_formatter_check(app_root_path)
      end)
    end

    @tag database: :postgresql
    test "has a passing test suite" do
      with_installer_tmp("app_with_defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "default_app")

        drop_test_database(app_root_path)
        assert_tests_pass(app_root_path)
      end)
    end
  end

  describe "phx.gen.html" do
    test "has no compilation or formatter warnings" do
      with_installer_tmp("app_with_defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "phx_blog")

        mix_run!(~w(phx.gen.html Blog Post posts title:unique body:string status:enum:unpublished:published:deleted), app_root_path)

        modify_file(Path.join(app_root_path, "lib/phx_blog_web/router.ex"), fn file ->
          inject_before_final_end(file, """

            scope "/", PhxBlogWeb do
              pipe_through [:browser]

              resources "/posts", PostController
            end
          """)
        end)

        assert_no_compilation_warnings(app_root_path)
        assert_passes_formatter_check(app_root_path)
      end)
    end

    @tag database: :postgresql
    test "has a passing test suite" do
      with_installer_tmp("app_with_defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "phx_blog")

        mix_run!(~w(phx.gen.html Blog Post posts title:unique body:string status:enum:unpublished:published:deleted order:integer:unique), app_root_path)

        modify_file(Path.join(app_root_path, "lib/phx_blog_web/router.ex"), fn file ->
          inject_before_final_end(file, """

            scope "/", PhxBlogWeb do
              pipe_through [:browser]

              resources "/posts", PostController
            end
          """)
        end)

        drop_test_database(app_root_path)
        assert_tests_pass(app_root_path)
      end)
    end
  end

  describe "phx.gen.json" do
    test "has no compilation or formatter warnings" do
      with_installer_tmp("app_with_defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "phx_blog")

        mix_run!(~w(phx.gen.json Blog Post posts title:unique body:string status:enum:unpublished:published:deleted), app_root_path)

        modify_file(Path.join(app_root_path, "lib/phx_blog_web/router.ex"), fn file ->
          inject_before_final_end(file, """

            scope "/api", PhxBlogWeb do
              pipe_through [:api]

              resources "/posts", PostController, except: [:new, :edit]
            end
          """)
        end)

        assert_no_compilation_warnings(app_root_path)
        assert_passes_formatter_check(app_root_path)
      end)
    end

    @tag database: :postgresql
    test "has a passing test suite" do
      with_installer_tmp("app_with_defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "phx_blog")

        mix_run!(~w(phx.gen.json Blog Post posts title:unique body:string status:enum:unpublished:published:deleted), app_root_path)

        modify_file(Path.join(app_root_path, "lib/phx_blog_web/router.ex"), fn file ->
          inject_before_final_end(file, """

            scope "/", PhxBlogWeb do
              pipe_through [:api]

              resources "/posts", PostController, except: [:new, :edit]
            end
          """)
        end)

        drop_test_database(app_root_path)
        assert_tests_pass(app_root_path)
      end)
    end
  end

  describe "phx.gen.live" do
    test "has no compilation or formatter warnings" do
      with_installer_tmp("app_with_defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "phx_blog", ["--live"])

        mix_run!(~w(phx.gen.live Blog Post posts title:unique body:string p:boolean s:enum:a:b:c), app_root_path)

        modify_file(Path.join(app_root_path, "lib/phx_blog_web/router.ex"), fn file ->
          inject_before_final_end(file, """

            scope "/", PhxBlogWeb do
              pipe_through [:browser]

              live "/posts", PostLive.Index, :index
              live "/posts/new", PostLive.Index, :new
              live "/posts/:id/edit", PostLive.Index, :edit

              live "/posts/:id", PostLive.Show, :show
              live "/posts/:id/show/edit", PostLive.Show, :edit
            end
          """)
        end)

        assert_no_compilation_warnings(app_root_path)
        assert_passes_formatter_check(app_root_path)
      end)
    end

    @tag database: :postgresql
    test "has a passing test suite" do
      with_installer_tmp("app_with_defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "phx_blog", ["--live"])

        mix_run!(~w(phx.gen.live Blog Post posts title body:string public:boolean status:enum:unpublished:published:deleted), app_root_path)

        modify_file(Path.join(app_root_path, "lib/phx_blog_web/router.ex"), fn file ->
          inject_before_final_end(file, """

            scope "/", PhxBlogWeb do
              pipe_through [:browser]

              live "/posts", PostLive.Index, :index
              live "/posts/new", PostLive.Index, :new
              live "/posts/:id/edit", PostLive.Index, :edit

              live "/posts/:id", PostLive.Show, :show
              live "/posts/:id/show/edit", PostLive.Show, :edit
            end
          """)
        end)

        drop_test_database(app_root_path)
        assert_tests_pass(app_root_path)
      end)
    end
  end

  describe "phx.gen.auth + bcrypt" do
    test "has no compilation or formatter warnings" do
      with_installer_tmp("new with defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "phx_blog")

        mix_run!(~w(phx.gen.auth Accounts User users), app_root_path)

        assert_no_compilation_warnings(app_root_path)
        assert_passes_formatter_check(app_root_path)
      end)
    end

    @tag database: :postgresql
    test "has a passing test suite" do
      with_installer_tmp("app_with_defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "default_app")

        mix_run!(~w(phx.gen.auth Accounts User users), app_root_path)

        drop_test_database(app_root_path)
        assert_tests_pass(app_root_path)
      end)
    end
  end
end

defmodule Phoenix.Integration.CodeGeneration.AppWithDefaultsTest do
  use Phoenix.Integration.CodeGeneratorCase, async: true

  describe "new with defaults" do
    test "has no compilation or formatter warnings" do
      with_installer_tmp("new with defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "phx_blog")

        assert_no_compilation_warnings(app_root_path)
        assert_passes_formatter_check(app_root_path)
      end)
    end

    @tag database: :postgresql
    test "has a passing test suite" do
      with_installer_tmp("app_with_defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "default_app")

        drop_test_database(app_root_path)
        assert_tests_pass(app_root_path)
      end)
    end
  end

  describe "phx.gen.html" do
    test "has no compilation or formatter warnings" do
      with_installer_tmp("app_with_defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "phx_blog")

        mix_run!(~w(phx.gen.html Blog Post posts title:unique body:string status:enum:unpublished:published:deleted), app_root_path)

        modify_file(Path.join(app_root_path, "lib/phx_blog_web/router.ex"), fn file ->
          inject_before_final_end(file, """

            scope "/", PhxBlogWeb do
              pipe_through [:browser]

              resources "/posts", PostController
            end
          """)
        end)

        assert_no_compilation_warnings(app_root_path)
        assert_passes_formatter_check(app_root_path)
      end)
    end

    @tag database: :postgresql
    test "has a passing test suite" do
      with_installer_tmp("app_with_defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "phx_blog")

        mix_run!(~w(phx.gen.html Blog Post posts title:unique body:string status:enum:unpublished:published:deleted order:integer:unique), app_root_path)

        modify_file(Path.join(app_root_path, "lib/phx_blog_web/router.ex"), fn file ->
          inject_before_final_end(file, """

            scope "/", PhxBlogWeb do
              pipe_through [:browser]

              resources "/posts", PostController
            end
          """)
        end)

        drop_test_database(app_root_path)
        assert_tests_pass(app_root_path)
      end)
    end
  end

  describe "phx.gen.json" do
    test "has no compilation or formatter warnings" do
      with_installer_tmp("app_with_defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "phx_blog")

        mix_run!(~w(phx.gen.json Blog Post posts title:unique body:string status:enum:unpublished:published:deleted), app_root_path)

        modify_file(Path.join(app_root_path, "lib/phx_blog_web/router.ex"), fn file ->
          inject_before_final_end(file, """

            scope "/", PhxBlogWeb do
              pipe_through [:api]

              resources "/posts", PostController, except: [:new, :edit]
            end
          """)
        end)

        assert_no_compilation_warnings(app_root_path)
        assert_passes_formatter_check(app_root_path)
      end)
    end

    @tag database: :postgresql
    test "has a passing test suite" do
      with_installer_tmp("app_with_defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "phx_blog")

        mix_run!(~w(phx.gen.json Blog Post posts title:unique body:string status:enum:unpublished:published:deleted), app_root_path)

        modify_file(Path.join(app_root_path, "lib/phx_blog_web/router.ex"), fn file ->
          inject_before_final_end(file, """

            scope "/", PhxBlogWeb do
              pipe_through [:api]

              resources "/posts", PostController, except: [:new, :edit]
            end
          """)
        end)

        drop_test_database(app_root_path)
        assert_tests_pass(app_root_path)
      end)
    end
  end

  describe "phx.gen.live" do
    test "has no compilation or formatter warnings" do
      with_installer_tmp("app_with_defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "phx_blog", ["--live"])

        mix_run!(~w(phx.gen.live Blog Post posts title:unique body:string p:boolean s:enum:a:b:c), app_root_path)

        modify_file(Path.join(app_root_path, "lib/phx_blog_web/router.ex"), fn file ->
          inject_before_final_end(file, """

            scope "/", PhxBlogWeb do
              pipe_through [:browser]

              live "/posts", PostLive.Index, :index
              live "/posts/new", PostLive.Index, :new
              live "/posts/:id/edit", PostLive.Index, :edit

              live "/posts/:id", PostLive.Show, :show
              live "/posts/:id/show/edit", PostLive.Show, :edit
            end
          """)
        end)

        assert_no_compilation_warnings(app_root_path)
        assert_passes_formatter_check(app_root_path)
      end)
    end

    @tag database: :postgresql
    test "has a passing test suite" do
      with_installer_tmp("app_with_defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "phx_blog", ["--live"])

        mix_run!(~w(phx.gen.live Blog Post posts title body:string public:boolean status:enum:unpublished:published:deleted), app_root_path)

        modify_file(Path.join(app_root_path, "lib/phx_blog_web/router.ex"), fn file ->
          inject_before_final_end(file, """

            scope "/", PhxBlogWeb do
              pipe_through [:browser]

              live "/posts", PostLive.Index, :index
              live "/posts/new", PostLive.Index, :new
              live "/posts/:id/edit", PostLive.Index, :edit

              live "/posts/:id", PostLive.Show, :show
              live "/posts/:id/show/edit", PostLive.Show, :edit
            end
          """)
        end)

        drop_test_database(app_root_path)
        assert_tests_pass(app_root_path)
      end)
    end
  end

  describe "phx.gen.auth + bcrypt" do
    test "has no compilation or formatter warnings" do
      with_installer_tmp("new with defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "phx_blog")

        mix_run!(~w(phx.gen.auth Accounts User users), app_root_path)

        assert_no_compilation_warnings(app_root_path)
        assert_passes_formatter_check(app_root_path)
      end)
    end

    @tag database: :postgresql
    test "has a passing test suite" do
      with_installer_tmp("app_with_defaults", fn tmp_dir ->
        {app_root_path, _} = generate_phoenix_app(tmp_dir, "default_app")

        mix_run!(~w(phx.gen.auth Accounts User users), app_root_path)

        drop_test_database(app_root_path)
        assert_tests_pass(app_root_path)
      end)
    end
  end
end


Update redirect to IncomingPhoneNumber API

defmodule ExTwilio.IncomingPhoneNumber do
  @moduledoc """
  Represents an IncomingPhoneNumber resource in the Twilio API.

  - [Twilio docs](https://www.twilio.com/docs/phone-numbers/api/incomingphonenumber-resource)
  """

  defstruct sid: nil,
            account_sid: nil,
            address_sid: nil,
            address_requirements: nil,
            api_version: nil,
            beta: nil,
            capabilities: nil,
            date_created: nil,
            date_updated: nil,
            friendly_name: nil,
            identity_sid: nil,
            phone_number: nil,
            origin: nil,
            sms_application_sid: nil,
            sms_fallback_method: nil,
            sms_fallback_url: nil,
            sms_method: nil,
            sms_url: nil,
            status: nil,
            status_callback: nil,
            status_callback_method: nil,
            trunk_sid: nil,
            uri: nil,
            voice_application_sid: nil,
            voice_caller_id_lookup: nil,
            voice_fallback_method: nil,
            voice_fallback_url: nil,
            voice_method: nil,
            voice_url: nil,
            emergency_status: nil,
            emergency_address_sid: nil

  use ExTwilio.Resource,
    import: [
      :stream,
      :all,
      :find,
      :create,
      :update,
      :destroy
    ]

  def parents, do: [:account]
end

defmodule ExTwilio.IncomingPhoneNumber do
  @moduledoc """
  Represents an IncomingPhoneNumber resource in the Twilio API.

  - [Twilio docs](https://www.twilio.com/docs/api/rest/incoming-phone-numbers)
  """

  defstruct sid: nil,
            account_sid: nil,
            address_sid: nil,
            address_requirements: nil,
            api_version: nil,
            beta: nil,
            capabilities: nil,
            date_created: nil,
            date_updated: nil,
            friendly_name: nil,
            identity_sid: nil,
            phone_number: nil,
            origin: nil,
            sms_application_sid: nil,
            sms_fallback_method: nil,
            sms_fallback_url: nil,
            sms_method: nil,
            sms_url: nil,
            status: nil,
            status_callback: nil,
            status_callback_method: nil,
            trunk_sid: nil,
            uri: nil,
            voice_application_sid: nil,
            voice_caller_id_lookup: nil,
            voice_fallback_method: nil,
            voice_fallback_url: nil,
            voice_method: nil,
            voice_url: nil,
            emergency_status: nil,
            emergency_address_sid: nil

  use ExTwilio.Resource,
    import: [
      :stream,
      :all,
      :find,
      :create,
      :update,
      :destroy
    ]

  def parents, do: [:account]
end


Fix issue with MapSet.new in Elixir < 1.2

defmodule Credo.Sources do
  alias Credo.SourceFile

  @default_sources_glob ~w(** *.{ex,exs})
  @stdin_filename "stdin"

  @doc """
  Finds sources for a given `Credo.Config`.

  Through the `files` key, configs may contain a list of `included` and `excluded`
  patterns. For `included`, patterns can be file paths, directory paths and globs.
  For `excluded`, patterns can also be specified as regular expressions.

  iex> Sources.find(%Credo.Config{files: %{excluded: ["not_me.ex"], included: ["*.ex"]}})

  iex> Sources.find(%Credo.Config{files: %{excluded: [/messy/], included: ["lib/mix", "root.ex"]}})

  """
  def find(%Credo.Config{files: %{excluded: [], included: [filename]}, read_from_stdin: true}) do
    filename |> source_file_from_stdin() |> List.wrap
  end
  def find(%Credo.Config{read_from_stdin: true}) do
    @stdin_filename |> source_file_from_stdin() |> List.wrap
  end
  def find(%Credo.Config{files: files}) do
    MapSet.new
    |> include(files.included)
    |> exclude(files.excluded)
    |> Enum.map(&to_source_file/1)
  end
  def find(paths) when is_list(paths) do
    paths
    |> Enum.flat_map(&find/1)
  end
  def find(path) when is_binary(path) do
    path |> recurse_path()
  end

  defp include(files, []), do: files
  defp include(files, [path | remaining_paths]) do
    include_paths = recurse_path(path) |> Enum.into(MapSet.new)

    files
    |> MapSet.union(include_paths)
    |> include(remaining_paths)
  end

  defp exclude(files, []), do: files
  defp exclude(files, [pattern | remaining_patterns]) when is_list(files) do
    files
    |> Enum.into(MapSet.new)
    |> exclude([pattern | remaining_patterns])
  end
  defp exclude(files, [pattern | remaining_patterns]) when is_binary(pattern) do
    exclude_paths = recurse_path(pattern) |> Enum.into(MapSet.new)

    files
    |> MapSet.difference(exclude_paths)
    |> exclude(remaining_patterns)
  end
  defp exclude(files, [pattern | remaining_patterns]) do
    files
    |> Enum.reject(&(String.match?(&1, pattern)))
    |> exclude(remaining_patterns)
  end

  defp recurse_path(path) do
    cond do
      File.regular?(path) ->
        [path]
      File.dir?(path) ->
        [path | @default_sources_glob]
        |> Path.join
        |> Path.wildcard
      true ->
        path
        |> Path.wildcard
        |> Enum.flat_map(&recurse_path/1)
    end
  end

  defp to_source_file(filename) do
    filename
    |> File.read!
    |> SourceFile.parse(filename)
  end

  defp source_file_from_stdin(filename) do
    read_from_stdin!()
    |> SourceFile.parse(filename)
  end

  defp read_from_stdin! do
    {:ok, source} = read_from_stdin()
    source
  end

  defp read_from_stdin(source \\ "") do
    case IO.read(:stdio, :line) do
      {:error, reason} -> {:error, reason}
      :eof             -> {:ok, source}
      data             -> source = source <> data
        read_from_stdin(source)
    end
  end
end

defmodule Credo.Sources do
  alias Credo.SourceFile

  @default_sources_glob ~w(** *.{ex,exs})
  @stdin_filename "stdin"

  @doc """
  Finds sources for a given `Credo.Config`.

  Through the `files` key, configs may contain a list of `included` and `excluded`
  patterns. For `included`, patterns can be file paths, directory paths and globs.
  For `excluded`, patterns can also be specified as regular expressions.

  iex> Sources.find(%Credo.Config{files: %{excluded: ["not_me.ex"], included: ["*.ex"]}})

  iex> Sources.find(%Credo.Config{files: %{excluded: [/messy/], included: ["lib/mix", "root.ex"]}})

  """
  def find(%Credo.Config{files: %{excluded: [], included: [filename]}, read_from_stdin: true}) do
    filename |> source_file_from_stdin() |> List.wrap
  end
  def find(%Credo.Config{read_from_stdin: true}) do
    @stdin_filename |> source_file_from_stdin() |> List.wrap
  end
  def find(%Credo.Config{files: files}) do
    MapSet.new
    |> include(files.included)
    |> exclude(files.excluded)
    |> Enum.map(&to_source_file/1)
  end
  def find(paths) when is_list(paths) do
    paths
    |> Enum.flat_map(&find/1)
  end
  def find(path) when is_binary(path) do
    path |> recurse_path()
  end

  defp include(files, []), do: files
  defp include(files, [path | remaining_paths]) do
    include_paths = recurse_path(path) |> Enum.into(MapSet.new)

    files
    |> MapSet.union(include_paths)
    |> include(remaining_paths)
  end

  defp exclude(files, []), do: files
  defp exclude(files, [pattern | remaining_patterns]) when is_list(files) do
    files
    |> MapSet.new
    |> exclude([pattern | remaining_patterns])
  end
  defp exclude(files, [pattern | remaining_patterns]) when is_binary(pattern) do
    exclude_paths = recurse_path(pattern) |> Enum.into(MapSet.new)

    files
    |> MapSet.difference(exclude_paths)
    |> exclude(remaining_patterns)
  end
  defp exclude(files, [pattern | remaining_patterns]) do
    files
    |> Enum.reject(&(String.match?(&1, pattern)))
    |> exclude(remaining_patterns)
  end

  defp recurse_path(path) do
    cond do
      File.regular?(path) ->
        [path]
      File.dir?(path) ->
        [path | @default_sources_glob]
        |> Path.join
        |> Path.wildcard
      true ->
        path
        |> Path.wildcard
        |> Enum.flat_map(&recurse_path/1)
    end
  end

  defp to_source_file(filename) do
    filename
    |> File.read!
    |> SourceFile.parse(filename)
  end

  defp source_file_from_stdin(filename) do
    read_from_stdin!()
    |> SourceFile.parse(filename)
  end

  defp read_from_stdin! do
    {:ok, source} = read_from_stdin()
    source
  end

  defp read_from_stdin(source \\ "") do
    case IO.read(:stdio, :line) do
      {:error, reason} -> {:error, reason}
      :eof             -> {:ok, source}
      data             -> source = source <> data
        read_from_stdin(source)
    end
  end
end


nvm, there was no CORS error, the server was just off.

defmodule Tcp do
	use Supervisor
	def atom_join(x, y) do
		a=to_string(x) <> "." <> to_string(y)
		String.to_atom(a)
	end
	def start_link(id, func1, func2) do
		import Supervisor.Spec, warn: false
		children = [
			worker(__MODULE__, [func1, id, "/", {0,0,0,0}], function: :run),
			worker(__MODULE__, [func2, :tcp_internal, "/priv/", {127,0,0,1}], function: :run, id: :tcp_internal),
		]
		opts = [strategy: :one_for_one, name: Tcp.Supervisor]
		Supervisor.start_link(children, opts)
	end
	def run(func, id, r, ip) do
		routes = [ {r, Tcp.Handler, [func, ip]}	]
		dispatch = :cowboy_router.compile([{:_, routes}])
		p = Port.port
		if id == :tcp_internal do p = p+1000 end
		opts = [port: p, ip: ip]
		env = [env: [dispatch: dispatch]]
		{:ok, _pid} = :cowboy.start_http(atom_join(:http, id), 100, opts, env)
	end
	def get_local(ip, port, x) do get(ip, port, x, '/priv') end
	def get(ip, port, a, y \\ '') do
		url = 'http://' ++ to_char_list(ip) ++ ':' ++ to_char_list(to_string(port)) ++ y
		content_type = 'application/octet-stream'
		request = {url, [], content_type, PackWrap.pack(a)}
		#:httpc.request(:post, request, [{:timeout, 2000}], [])#hopefully 2 seconds?
		x = :httpc.request(:post, request, [], [])
		case x do
			{:ok, z} ->
				b = z |> elem(2)
				if b == [] do [] else PackWrap.unpack(b) end
			{:error, :socket_closed_remotely} -> get(ip, port, a, y)
			{:error, {:failed_connect, _}} -> nil
			x ->
				IO.puts("TCP WEIRD #{inspect x}")
				x
		end
	end
end
defmodule Tcp.Handler do
	def init({:tcp, :http}, req, opts) do
		{:ok, req, opts}
	end
	def body(req, opts) do
		[func, ip] = opts
		{status, data, req2} = :cowboy_req.body(req, opts)
		cond do
			status == :ok -> data
			true ->
				:timer.sleep(50)
				body(req, opts)
		end
	end
	def handle(req, opts) do
		[func, ip] = opts
		f = fn(x) -> tl(x) end
		if ip == {127,0,0,1} do
			f = fn(x) -> tl(tl(tl(tl(tl(tl(x)))))) end
		end
		headers = [{"content-type", "application/octet-stream"},
							 {"Access-Control-Allow-Origin", "*"}]
		#{"Access-Control-Allow-Methods", "GET, POST"},
		#{"Access-Control-Allow-Credentials", "true"}]

		b = body(req, opts) |> PackWrap.unpack |> func.() |> PackWrap.pack
		{:ok, resp} = :cowboy_req.reply(200, headers, b, req)
		{:ok, resp, opts}
	end
	def terminate(_reason, _req, _state) do
		:ok
	end
end

defmodule Tcp do
	use Supervisor
	def atom_join(x, y) do
		a=to_string(x) <> "." <> to_string(y)
		String.to_atom(a)
	end
	def start_link(id, func1, func2) do
		import Supervisor.Spec, warn: false
		children = [
			worker(__MODULE__, [func1, id, "/", {0,0,0,0}], function: :run),
			worker(__MODULE__, [func2, :tcp_internal, "/priv/", {127,0,0,1}], function: :run, id: :tcp_internal),
		]
		opts = [strategy: :one_for_one, name: Tcp.Supervisor]
		Supervisor.start_link(children, opts)
	end
	def run(func, id, r, ip) do
		routes = [ {r, Tcp.Handler, [func, ip]}	]
		dispatch = :cowboy_router.compile([{:_, routes}])
		p = Port.port
		if id == :tcp_internal do p = p+1000 end
		opts = [port: p, ip: ip]
		env = [env: [dispatch: dispatch]]
		{:ok, _pid} = :cowboy.start_http(atom_join(:http, id), 100, opts, env)
	end
	def get_local(ip, port, x) do get(ip, port, x, '/priv') end
	def get(ip, port, a, y \\ '') do
		url = 'http://' ++ to_char_list(ip) ++ ':' ++ to_char_list(to_string(port)) ++ y
		content_type = 'application/octet-stream'
		request = {url, [], content_type, PackWrap.pack(a)}
		#:httpc.request(:post, request, [{:timeout, 2000}], [])#hopefully 2 seconds?
		x = :httpc.request(:post, request, [], [])
		case x do
			{:ok, z} ->
				b = z |> elem(2)
				if b == [] do [] else PackWrap.unpack(b) end
			{:error, :socket_closed_remotely} -> get(ip, port, a, y)
			{:error, {:failed_connect, _}} -> nil
			x ->
				IO.puts("TCP WEIRD #{inspect x}")
				x
		end
	end
end
defmodule Tcp.Handler do
	def init({:tcp, :http}, req, opts) do
		{:ok, req, opts}
	end
	def body(req, opts) do
		[func, ip] = opts
		{status, data, req2} = :cowboy_req.body(req, opts)
		cond do
			status == :ok -> data
			true ->
				:timer.sleep(50)
				body(req, opts)
		end
	end
	def handle(req, opts) do
		[func, ip] = opts
		f = fn(x) -> tl(x) end
		if ip == {127,0,0,1} do
			f = fn(x) -> tl(tl(tl(tl(tl(tl(x)))))) end
		end
		headers = [{"content-type", "application/octet-stream"},
							 {"Access-Control-Allow-Origin", "*"},
							 {"Access-Control-Allow-Methods", "GET, POST"},
							 {"Access-Control-Allow-Credentials", "true"}]

		b = body(req, opts) |> PackWrap.unpack |> func.() |> PackWrap.pack
		{:ok, resp} = :cowboy_req.reply(200, headers, b, req)
		{:ok, resp, opts}
	end
	def terminate(_reason, _req, _state) do
		:ok
	end
end


Attempt to get setup.farm.bot working again

defmodule FarmbotOS.Platform.Target.Configurator.CaptivePortal do
  @moduledoc """
  Handles turning Farmbot's internal network representation into
  either a VintageNet.Technology.Ethernet or VintageNet.Technology.WiFi
  RawConfig.
  """
  @behaviour VintageNet.Technology
  require FarmbotCore.Logger

  @impl VintageNet.Technology
  def normalize(config) do
    %{config | type: VintageNet.Technology.WiFi}
    |> VintageNet.Technology.WiFi.normalize()
  end

  @impl VintageNet.Technology
  def to_raw_config(ifname, config, opts) do
    {:ok, normalized} = normalize(config)

    ifname
    |> vintage_wifi(normalized, opts)
    |> dnsmasq(opts)
  end

  @impl VintageNet.Technology
  def check_system(opts) do
    VintageNet.Technology.WiFi.check_system(opts)
  end

  @impl true
  def ioctl(ifname, ioctl, args) do
    VintageNet.Technology.WiFi.ioctl(ifname, ioctl, args)
  end

  defp dnsmasq(%{ifname: ifname, source_config: %{dnsmasq: config}} = raw_config, opts) do
    tmpdir = Keyword.fetch!(opts, :tmpdir)
    killall = Keyword.fetch!(opts, :bin_killall)
    dnsmasq = System.find_executable("dnsmasq")
    dnsmasq_conf_path = Path.join(tmpdir, "dnsmasq.conf.#{ifname}")
    dnsmasq_lease_file = Path.join(tmpdir, "dnsmasq.leases.#{ifname}")
    dnsmasq_pid_file = Path.join(tmpdir, "dnsmasq.pid.#{ifname}")

    dnsmasq_conf_contents = """
    interface=#{ifname}
    server=#{config[:address]}
    address=/#/#{config[:address]}
    dhcp-option=6,#{config[:address]}
    dhcp-range=#{config[:start]},#{config[:end]},12h
    """

    files = [
      {dnsmasq_conf_path, dnsmasq_conf_contents}
    ]

    up_cmds = [
      {:run, dnsmasq,
       [
         "-K",
         "-l",
         dnsmasq_lease_file,
         "-x",
         dnsmasq_pid_file,
         "-C",
         dnsmasq_conf_path
       ]}
    ]

    down_cmds = [
      {:run, killall, ["-q", "-9", "dnsmasq"]}
    ]

    updated_raw_config = %{
      raw_config
      | files: raw_config.files ++ files,
        up_cmds: raw_config.up_cmds ++ up_cmds,
        down_cmds: raw_config.down_cmds ++ down_cmds,
        cleanup_files:
          raw_config.cleanup_files ++ [dnsmasq_conf_path, dnsmasq_lease_file, dnsmasq_pid_file]
    }

    {:ok, updated_raw_config}
  end

  defp dnsmasq(%{} = raw_config, _opts) do
    FarmbotCore.Logger.error(1, "DNSMASQ Disabled")
    {:ok, raw_config}
  end

  defp vintage_wifi(ifname, config, opts) do
    with {:ok, config} <- VintageNet.Technology.WiFi.normalize(config),
         {:ok, raw_config} <- VintageNet.Technology.WiFi.to_raw_config(ifname, config, opts) do
      %{raw_config | type: VintageNet.Technology.WiFi}
    end
  end
end

defmodule FarmbotOS.Platform.Target.Configurator.CaptivePortal do
  @moduledoc """
  Handles turning Farmbot's internal network representation into
  either a VintageNet.Technology.Ethernet or VintageNet.Technology.WiFi
  RawConfig.
  """
  @behaviour VintageNet.Technology
  require FarmbotCore.Logger

  @impl VintageNet.Technology
  def normalize(config) do
    %{config | type: VintageNet.Technology.WiFi}
    |> VintageNet.Technology.WiFi.normalize()
  end

  @impl VintageNet.Technology
  def to_raw_config(ifname, config, opts) do
    {:ok, normalized} = normalize(config)

    ifname
    |> vintage_wifi(normalized, opts)
    |> dnsmasq(opts)
  end

  @impl VintageNet.Technology
  def check_system(opts) do
    VintageNet.Technology.WiFi.check_system(opts)
  end

  @impl true
  def ioctl(ifname, ioctl, args) do
    VintageNet.Technology.WiFi.ioctl(ifname, ioctl, args)
  end

  defp dnsmasq(%{ifname: ifname, source_config: %{dnsmasq: config}} = raw_config, opts) do
    tmpdir = Keyword.fetch!(opts, :tmpdir)
    killall = Keyword.fetch!(opts, :bin_killall)
    dnsmasq = System.find_executable("dnsmasq")
    dnsmasq_conf_path = Path.join(tmpdir, "dnsmasq.conf.#{ifname}")
    dnsmasq_lease_file = Path.join(tmpdir, "dnsmasq.leases.#{ifname}")
    dnsmasq_pid_file = Path.join(tmpdir, "dnsmasq.pid.#{ifname}")

    dnsmasq_conf_contents = """
    interface=#{ifname}
    address=/#/#{config[:address]}
    dhcp-option=3,#{config[:address]}
    dhcp-option=6,#{config[:address]}
    dhcp-range=#{config[:start]},#{config[:end]},12h
    """

    files = [
      {dnsmasq_conf_path, dnsmasq_conf_contents}
    ]

    up_cmds = [
      {:run, dnsmasq,
       [
         "-K",
         "-l",
         dnsmasq_lease_file,
         "-x",
         dnsmasq_pid_file,
         "-C",
         dnsmasq_conf_path
       ]}
    ]

    down_cmds = [
      {:run, killall, ["-q", "-9", "dnsmasq"]}
    ]

    updated_raw_config = %{
      raw_config
      | files: raw_config.files ++ files,
        up_cmds: raw_config.up_cmds ++ up_cmds,
        down_cmds: raw_config.down_cmds ++ down_cmds,
        cleanup_files:
          raw_config.cleanup_files ++ [dnsmasq_conf_path, dnsmasq_lease_file, dnsmasq_pid_file]
    }

    {:ok, updated_raw_config}
  end

  defp dnsmasq(%{} = raw_config, _opts) do
    FarmbotCore.Logger.error(1, "DNSMASQ Disabled")
    {:ok, raw_config}
  end

  defp vintage_wifi(ifname, config, opts) do
    with {:ok, config} <- VintageNet.Technology.WiFi.normalize(config),
         {:ok, raw_config} <- VintageNet.Technology.WiFi.to_raw_config(ifname, config, opts) do
      %{raw_config | type: VintageNet.Technology.WiFi}
    end
  end
end


Sleep after sending restart command

Sleep after sending restart command

defmodule Config.Provider do
  @moduledoc """
  Specifies a provider API that loads configuration during boot.

  Config providers are typically used during releases to load
  external configuration while the system boots. This is done
  by starting the VM with the minimum amount of applications
  running, then invoking all of the providers, and then
  restarting the system. This requires a mutable configuration
  file on disk, as the results of the providers are written to
  the file system. For more information on runtime configuration,
  see `mix release`.

  ## Sample config provider

  For example, imagine you need to load some configuration from
  a JSON file and load that into the system. Said configuration
  provider would look like:

      defmodule JSONConfigProvider do
        @behaviour Config.Provider

        # Let's pass the path to the JSON file as config
        def init(path) when is_binary(path), do: path

        def load(config, path) do
          # We need to start any app we may depend on.
          {:ok, _} = Application.ensure_all_started(:jason)

          json = path |> File.read!() |> Jason.decode!()

          Config.Reader.merge(
            config,
            my_app: [
              some_value: json["my_app_some_value"],
              another_value: json["my_app_another_value"],
            ]
          )
        end
      end

  Then when specifying your release, you can specify the provider:

      config_providers: [{JSONConfigProvider, "/etc/config.json"}]

  Now once the system boots, it will invoke the provider early in
  the boot process, save the merged configuration to the disk, and
  reboot the system with the new values in place.
  """

  @type config :: keyword
  @type state :: term

  @typedoc """
  A path pointing to a configuration file.

  Since configuration files are often accessed on target machines,
  it can be expressed either as:

    * a binary representing an absolute path

    * a `{:system, system_var, path}` tuple where the config is the
      concatenation of the environment variable `system_var` with
      the given `path`

  """
  @type config_path :: {:system, binary(), binary()} | binary()

  @doc """
  Invoked when initializing a config provider.

  A config provider is typically initialized on the machine
  where the system is assembled and not on the target machine.
  The `c:init/1` callback is useful to verify the arguments
  given to the provider and prepare the state that will be
  given to `c:load/2`.

  Furthermore, because the state returned by `c:init/1` can
  be written to text-based config files, it should be
  restricted only to simple data types, such as integers,
  strings, atoms, tuples, maps, and lists. Entries such as
  PIDs, references, and functions cannot be serialized.
  """
  @callback init(term) :: state

  @doc """
  Loads configuration (typically during system boot).

  It receives the current `config` and the `state` returned by
  `c:init/1`. Then you typically read the extra configuration
  from an external source and merge it into the received `config`.
  Merging should be done with `Config.Reader.merge/2`, as it
  performs deep merge. It should return the updated config.

  Note that `c:load/2` is typically invoked very early in the
  boot process, therefore if you need to use an application
  in the provider, it is your responsibility to start it.
  """
  @callback load(config, state) :: config

  @doc false
  defstruct [:providers, :config_path, extra_config: [], prune_after_boot: false]

  @doc """
  Validates a `t:config_path/0`.
  """
  @doc since: "1.9.0"
  @spec validate_config_path!(config_path) :: :ok
  def validate_config_path!({:system, name, path})
      when is_binary(name) and is_binary(path),
      do: :ok

  def validate_config_path!(path) do
    if is_binary(path) and Path.type(path) != :relative do
      :ok
    else
      raise ArgumentError, """
      expected configuration path to be:

        * a binary representing an absolute path
        * a tuple {:system, system_var, path} where the config is the \
      concatenation of the `system_var` with the given `path`

      Got: #{inspect(path)}
      """
    end
  end

  @doc """
  Resolves a `t:config_path/0` to an actual path.
  """
  @doc since: "1.9.0"
  @spec resolve_config_path!(config_path) :: binary
  def resolve_config_path!(path) when is_binary(path), do: path
  def resolve_config_path!({:system, name, path}), do: System.fetch_env!(name) <> path

  @doc false
  def init(providers, config_path, opts \\ []) when is_list(providers) and is_list(opts) do
    validate_config_path!(config_path)
    providers = for {provider, init} <- providers, do: {provider, provider.init(init)}
    struct!(%Config.Provider{config_path: config_path, providers: providers}, opts)
  end

  @doc false
  def boot(app, key, restart_fun \\ &restart_and_sleep/0) do
    # The app with the config provider settings may not
    # have been loaded at this point, so make sure we load
    # its environment before querying it.
    _ = :application.load(app)

    # The config provider typically runs very early in the
    # release process, so we need to make sure Elixir is started
    # before we go around running Elixir code.
    {:ok, _} = :application.ensure_all_started(:elixir)

    case :application.get_env(app, key) do
      {:ok, %Config.Provider{} = provider} ->
        path = resolve_config_path!(provider.config_path)
        validate_no_cyclic_boot!(path)

        read_config!(path)
        |> Config.__merge__([{app, [{key, booted_key(provider, path)}]} | provider.extra_config])
        |> run_providers(provider)
        |> write_config!(path)

        restart_fun.()

      {:ok, {:booted, path}} ->
        File.rm(path)
        :booted

      {:ok, :booted} ->
        :booted

      _ ->
        :skip
    end
  end

  defp restart_and_sleep do
    :init.restart()
    Process.sleep(:infinity)
  end

  defp booted_key(%{prune_after_boot: true}, path), do: {:booted, path}
  defp booted_key(%{prune_after_boot: false}, _path), do: :booted

  defp validate_no_cyclic_boot!(path) do
    if System.get_env("ELIXIR_CONFIG_PROVIDER_BOOTED") do
      bad_path_abort("Got infinite loop when running Config.Provider", path)
    else
      System.put_env("ELIXIR_CONFIG_PROVIDER_BOOTED", "1")
    end
  end

  defp read_config!(path) do
    case :file.consult(path) do
      {:ok, [inner]} ->
        inner

      {:error, reason} ->
        bad_path_abort(
          "Could not read runtime configuration due to reason: #{inspect(reason)}",
          path
        )
    end
  end

  defp run_providers(config, %{providers: providers}) do
    Enum.reduce(providers, config, fn {provider, state}, acc ->
      try do
        provider.load(acc, state)
      catch
        kind, error ->
          IO.puts(:stderr, "ERROR! Config provider #{inspect(provider)} failed with:")
          IO.puts(:stderr, Exception.format(kind, error, __STACKTRACE__))
          :erlang.raise(kind, error, __STACKTRACE__)
      else
        term when is_list(term) ->
          term

        term ->
          abort("Expected provider #{inspect(provider)} to return a list, got: #{inspect(term)}")
      end
    end)
  end

  defp write_config!(config, path) do
    contents = :io_lib.format("%% coding: utf-8~n~tw.~n", [config])

    case File.write(path, contents, [:utf8]) do
      :ok ->
        :ok

      {:error, reason} ->
        bad_path_abort(
          "Could not write runtime configuration due to reason: #{inspect(reason)}",
          path
        )
    end
  end

  defp bad_path_abort(msg, path) do
    abort(
      msg <>
        ". Please make sure #{inspect(path)} is writable and accessible " <>
        "or choose a different path"
    )
  end

  defp abort(msg) do
    IO.puts(:stderr, "ERROR! " <> msg)
    raise(msg)
  end
end

defmodule Config.Provider do
  @moduledoc """
  Specifies a provider API that loads configuration during boot.

  Config providers are typically used during releases to load
  external configuration while the system boots. This is done
  by starting the VM with the minimum amount of applications
  running, then invoking all of the providers, and then
  restarting the system. This requires a mutable configuration
  file on disk, as the results of the providers are written to
  the file system. For more information on runtime configuration,
  see `mix release`.

  ## Sample config provider

  For example, imagine you need to load some configuration from
  a JSON file and load that into the system. Said configuration
  provider would look like:

      defmodule JSONConfigProvider do
        @behaviour Config.Provider

        # Let's pass the path to the JSON file as config
        def init(path) when is_binary(path), do: path

        def load(config, path) do
          # We need to start any app we may depend on.
          {:ok, _} = Application.ensure_all_started(:jason)

          json = path |> File.read!() |> Jason.decode!()

          Config.Reader.merge(
            config,
            my_app: [
              some_value: json["my_app_some_value"],
              another_value: json["my_app_another_value"],
            ]
          )
        end
      end

  Then when specifying your release, you can specify the provider:

      config_providers: [{JSONConfigProvider, "/etc/config.json"}]

  Now once the system boots, it will invoke the provider early in
  the boot process, save the merged configuration to the disk, and
  reboot the system with the new values in place.
  """

  @type config :: keyword
  @type state :: term

  @typedoc """
  A path pointing to a configuration file.

  Since configuration files are often accessed on target machines,
  it can be expressed either as:

    * a binary representing an absolute path

    * a `{:system, system_var, path}` tuple where the config is the
      concatenation of the environment variable `system_var` with
      the given `path`

  """
  @type config_path :: {:system, binary(), binary()} | binary()

  @doc """
  Invoked when initializing a config provider.

  A config provider is typically initialized on the machine
  where the system is assembled and not on the target machine.
  The `c:init/1` callback is useful to verify the arguments
  given to the provider and prepare the state that will be
  given to `c:load/2`.

  Furthermore, because the state returned by `c:init/1` can
  be written to text-based config files, it should be
  restricted only to simple data types, such as integers,
  strings, atoms, tuples, maps, and lists. Entries such as
  PIDs, references, and functions cannot be serialized.
  """
  @callback init(term) :: state

  @doc """
  Loads configuration (typically during system boot).

  It receives the current `config` and the `state` returned by
  `c:init/1`. Then you typically read the extra configuration
  from an external source and merge it into the received `config`.
  Merging should be done with `Config.Reader.merge/2`, as it
  performs deep merge. It should return the updated config.

  Note that `c:load/2` is typically invoked very early in the
  boot process, therefore if you need to use an application
  in the provider, it is your responsibility to start it.
  """
  @callback load(config, state) :: config

  @doc false
  defstruct [:providers, :config_path, extra_config: [], prune_after_boot: false]

  @doc """
  Validates a `t:config_path/0`.
  """
  @doc since: "1.9.0"
  @spec validate_config_path!(config_path) :: :ok
  def validate_config_path!({:system, name, path})
      when is_binary(name) and is_binary(path),
      do: :ok

  def validate_config_path!(path) do
    if is_binary(path) and Path.type(path) != :relative do
      :ok
    else
      raise ArgumentError, """
      expected configuration path to be:

        * a binary representing an absolute path
        * a tuple {:system, system_var, path} where the config is the \
      concatenation of the `system_var` with the given `path`

      Got: #{inspect(path)}
      """
    end
  end

  @doc """
  Resolves a `t:config_path/0` to an actual path.
  """
  @doc since: "1.9.0"
  @spec resolve_config_path!(config_path) :: binary
  def resolve_config_path!(path) when is_binary(path), do: path
  def resolve_config_path!({:system, name, path}), do: System.fetch_env!(name) <> path

  @doc false
  def init(providers, config_path, opts \\ []) when is_list(providers) and is_list(opts) do
    validate_config_path!(config_path)
    providers = for {provider, init} <- providers, do: {provider, provider.init(init)}
    struct!(%Config.Provider{config_path: config_path, providers: providers}, opts)
  end

  @doc false
  def boot(app, key, restart_fun \\ &System.restart/0) do
    # The app with the config provider settings may not
    # have been loaded at this point, so make sure we load
    # its environment before querying it.
    _ = :application.load(app)

    # The config provider typically runs very early in the
    # release process, so we need to make sure Elixir is started
    # before we go around running Elixir code.
    {:ok, _} = :application.ensure_all_started(:elixir)

    case :application.get_env(app, key) do
      {:ok, %Config.Provider{} = provider} ->
        path = resolve_config_path!(provider.config_path)
        validate_no_cyclic_boot!(path)

        read_config!(path)
        |> Config.__merge__([{app, [{key, booted_key(provider, path)}]} | provider.extra_config])
        |> run_providers(provider)
        |> write_config!(path)

        restart_fun.()

      {:ok, {:booted, path}} ->
        File.rm(path)
        :booted

      {:ok, :booted} ->
        :booted

      _ ->
        :skip
    end
  end

  defp booted_key(%{prune_after_boot: true}, path), do: {:booted, path}
  defp booted_key(%{prune_after_boot: false}, _path), do: :booted

  defp validate_no_cyclic_boot!(path) do
    if System.get_env("ELIXIR_CONFIG_PROVIDER_BOOTED") do
      bad_path_abort("Got infinite loop when running Config.Provider", path)
    else
      System.put_env("ELIXIR_CONFIG_PROVIDER_BOOTED", "1")
    end
  end

  defp read_config!(path) do
    case :file.consult(path) do
      {:ok, [inner]} ->
        inner

      {:error, reason} ->
        bad_path_abort(
          "Could not read runtime configuration due to reason: #{inspect(reason)}",
          path
        )
    end
  end

  defp run_providers(config, %{providers: providers}) do
    Enum.reduce(providers, config, fn {provider, state}, acc ->
      try do
        provider.load(acc, state)
      catch
        kind, error ->
          IO.puts(:stderr, "ERROR! Config provider #{inspect(provider)} failed with:")
          IO.puts(:stderr, Exception.format(kind, error, __STACKTRACE__))
          :erlang.raise(kind, error, __STACKTRACE__)
      else
        term when is_list(term) ->
          term

        term ->
          abort("Expected provider #{inspect(provider)} to return a list, got: #{inspect(term)}")
      end
    end)
  end

  defp write_config!(config, path) do
    contents = :io_lib.format("%% coding: utf-8~n~tw.~n", [config])

    case File.write(path, contents, [:utf8]) do
      :ok ->
        :ok

      {:error, reason} ->
        bad_path_abort(
          "Could not write runtime configuration due to reason: #{inspect(reason)}",
          path
        )
    end
  end

  defp bad_path_abort(msg, path) do
    abort(
      msg <>
        ". Please make sure #{inspect(path)} is writable and accessible " <>
        "or choose a different path"
    )
  end

  defp abort(msg) do
    IO.puts(:stderr, "ERROR! " <> msg)
    raise(msg)
  end
end


Report status in latency metric

Report status in latency metric

defmodule Gateway.Plugins.Monitoring do
  @moduledoc """
  This plugin measures or receives performance metrics from other parts of Annon and sends them to StatsD server.

  It is enabled by default and can not be disabled without rebuilding Annon container.

  It can be used with [DataDog agent](http://datadoghq.com).
  """
  use Gateway.Helpers.Plugin,
    plugin_name: "monitoring"

  alias Plug.Conn
  import Gateway.Helpers.Latency

  @doc false
  def call(%Conn{} = conn, _opts) do
    api_tags = tags(conn)

    conn
    |> get_request_size()
    |> ExStatsD.histogram("request_size", tags: api_tags)

    ExStatsD.increment("request_count", tags: api_tags)

    conn
    |> Conn.register_before_send(&write_metrics(&1))
  end

  defp write_metrics(%Conn{} = conn) do
    client_req_start_time = Map.get(conn.assigns, :client_req_start_time)
    conn = write_latency(conn, :latencies_client, client_req_start_time)
    request_duration = conn.assigns.latencies_client - Map.get(conn.assigns, :latencies_upstream, 0)
    api_tags = tags(conn) ++ ["http_status:#{to_string conn.status}"]

    ExStatsD.timer(request_duration, "latency", tags: api_tags)
    ExStatsD.increment("response_count", tags: api_tags)

    conn
    |> Conn.assign(:latencies_gateway, request_duration)
  end

  defp tags(%Conn{host: host, method: method, port: port} = conn),
    do: ["http_host:#{to_string host}",
         "http_method:#{to_string method}",
         "http_port:#{to_string port}"] ++ api_tags(conn)

  defp api_tags(%Conn{private: %{api_config: api_name, id: api_id}}),
    do: ["api_name:#{to_string api_name}", "api_id:#{to_string api_id}"]
  defp api_tags(_),
    do: ["api_name:unknown", "api_id:unknown"]

  defp get_request_size(conn) do
    get_headers_size(conn) + get_body_size(conn) + get_query_string_size(conn)
  end

  defp get_headers_size(%Conn{req_headers: req_headers}) do
    req_headers
    |> Enum.map(&Tuple.to_list(&1))
    |> List.flatten
    |> Enum.join
    |> byte_size
  end

  defp get_body_size(conn) do
    conn
    |> Conn.read_body
    |> elem(1)
    |> byte_size
  end

  defp get_query_string_size(%Conn{query_string: query_string}) do
    query_string
    |> byte_size
  end
end

defmodule Gateway.Plugins.Monitoring do
  @moduledoc """
  This plugin measures or receives performance metrics from other parts of Annon and sends them to StatsD server.

  It is enabled by default and can not be disabled without rebuilding Annon container.

  It can be used with [DataDog agent](http://datadoghq.com).
  """
  use Gateway.Helpers.Plugin,
    plugin_name: "monitoring"

  alias Plug.Conn
  import Gateway.Helpers.Latency

  @doc false
  def call(%Conn{} = conn, _opts) do
    api_tags = tags(conn)

    conn
    |> get_request_size()
    |> ExStatsD.histogram("request_size", tags: api_tags)

    ExStatsD.increment("request_count", tags: api_tags)

    conn
    |> Conn.register_before_send(&write_metrics(&1))
  end

  defp write_metrics(%Conn{} = conn) do
    client_req_start_time = Map.get(conn.assigns, :client_req_start_time)
    conn = write_latency(conn, :latencies_client, client_req_start_time)
    request_duration = conn.assigns.latencies_client - Map.get(conn.assigns, :latencies_upstream, 0)
    api_tags = tags(conn)

    ExStatsD.timer(request_duration, "latency", tags: api_tags)
    ExStatsD.increment("response_count", tags: api_tags ++ ["http_status:#{to_string conn.status}"])

    conn
    |> Conn.assign(:latencies_gateway, request_duration)
  end

  defp tags(%Conn{host: host, method: method, port: port} = conn),
    do: ["http_host:#{to_string host}",
         "http_method:#{to_string method}",
         "http_port:#{to_string port}"] ++ api_tags(conn)

  defp api_tags(%Conn{private: %{api_config: api_name, id: api_id}}),
    do: ["api_name:#{to_string api_name}", "api_id:#{to_string api_id}"]
  defp api_tags(_),
    do: ["api_name:unknown", "api_id:unknown"]

  defp get_request_size(conn) do
    get_headers_size(conn) + get_body_size(conn) + get_query_string_size(conn)
  end

  defp get_headers_size(%Conn{req_headers: req_headers}) do
    req_headers
    |> Enum.map(&Tuple.to_list(&1))
    |> List.flatten
    |> Enum.join
    |> byte_size
  end

  defp get_body_size(conn) do
    conn
    |> Conn.read_body
    |> elem(1)
    |> byte_size
  end

  defp get_query_string_size(%Conn{query_string: query_string}) do
    query_string
    |> byte_size
  end
end


Fix the compare links that get dropped into slack

[Fixes #148775643](https://www.pivotaltracker.com/story/show/148775643)

defmodule Ello.Serve.VersionStore.SlackNotifications do

  def new_version("webapp", ver) do
    Task.async fn ->
      notify_slack(%{
        text: "New version of `webapp` available: #{version_link(ver)}",
        attachments: [
          %{
            title: "Compare",
            text: """
            Compare to the active version in each env:
            #{compare_link(ver, "stage1")} #{compare_link(ver, "stage2")} #{compare_link(ver, "ninja")} #{compare_link(ver, "rainbow")} #{compare_link(ver, "production")}
            """,
            color: "#0366d6",
          },
          %{
            title: "Preview",
            text: """
            Previews are available only by accessing the app with the url with the version link. You will continue seeing the preview version until you refresh.
            You may preview in any environment:
            <https://ello-fg-stage1.herokuapp.com?version=#{ver}|stage1> <https://ello-fg-stage2.herokuapp.com?version=#{ver}|stage2> <https://ello.ninja?version=#{ver}|ninja> <https://ello-fg-rainbow.herokuapp.com?version=#{ver}|rainbow> <https://ello.co?version=#{ver}|production>
            """,
            color: "good",
          },
          %{
            title: "Publish",
            text: "Publishing makes this version of webapp active for all users visiting the environment.\n\nPublish with caution.",
            color: "warning",
            callback_id: "publish:webapp",
            actions: [
              %{
                name: "stage1",
                text: "stage1",
                value: ver,
                type: "button"
              },
              %{
                name: "stage2",
                text: "stage2",
                value: ver,
                type: "button"
              },
              %{
                name: "ninja",
                text: "ninja",
                value: ver,
                type: "button"
              },
              %{
                name: "rainbow",
                text: "rainbow",
                value: ver,
                type: "button"
              },
              %{
                name: "production",
                text: "production",
                value: ver,
                style: "danger",
                type: "button",
                confirm: %{
                  title: "Are you sure?",
                  text: "Publishing this version will push it to production for all users.",
                  ok_text: "I got this",
                  dismiss_text: "Nope."
                }
              },
            ]
          }
        ]
      })
    end
  end

  def version_activated("webapp", ver, env, nil) do
    Task.async fn ->
      notify_slack(%{
        attachments: [
          %{
            title: "New version published for all users on #{env}.",
            text:  "Version #{version_link(ver)}",
            color: "good",
          }
        ]
      })
    end
  end

  def version_activated("webapp", ver, env, previous) do
    Task.async fn ->
      notify_slack(%{
        attachments: [
          %{
            title: "New version published for all users on #{env}.",
            text:  "Version #{version_link(ver)}",
            color: "good",
          },
          %{
            title: "Rollback",
            text: "Rollback version #{version_link(ver)} to #{version_link(previous)} on #{env}?",
            color: "warning",
            callback_id: "publish:webapp",
            actions: [
              %{
                name: env,
                text: "Rollback!",
                value: previous,
                style: "danger",
                type: "button",
                confirm: %{
                  title: "Are you sure?",
                  text: "You are about to rollback this version for all users.",
                  ok_text: "I got this",
                  dismiss_text: "Nope.",
                },
              },
            ],
          },
        ],
      })
    end
  end

  def notify_slack(body) do
    case webhook_url() do
      nil -> :ok
      url ->
        HTTPoison.post!(url, Poison.encode!(body), [], [])
    end
  end

  defp webhook_url do
    Application.get_env(:ello_serve, :slack_webhook_url)
  end

  defp compare_link(ver, env) do
    case Ello.Serve.VersionStore.version_history("webapp", env) do
      [prev | _] -> "<https://github.com/ello/webapp/compare/#{prev}...#{ver}|#{env}>"
      _             -> ""
    end
  end

  def version_link(ver) do
    nice = String.slice(ver, 0, 7)
    "<https://github.com/ello/webapp/commit/#{ver}|#{nice}>"
  end
end

defmodule Ello.Serve.VersionStore.SlackNotifications do

  def new_version("webapp", ver) do
    Task.async fn ->
      notify_slack(%{
        text: "New version of `webapp` available: #{version_link(ver)}",
        attachments: [
          %{
            title: "Compare",
            text: """
            Compare to the active version in each env:
            #{compare_link(ver, "stage1")} #{compare_link(ver, "stage2")} #{compare_link(ver, "ninja")} #{compare_link(ver, "rainbow")} #{compare_link(ver, "production")}
            """,
            color: "#0366d6",
          },
          %{
            title: "Preview",
            text: """
            Previews are available only by accessing the app with the url with the version link. You will continue seeing the preview version until you refresh.
            You may preview in any environment:
            <https://ello-fg-stage1.herokuapp.com?version=#{ver}|stage1> <https://ello-fg-stage2.herokuapp.com?version=#{ver}|stage2> <https://ello.ninja?version=#{ver}|ninja> <https://ello-fg-rainbow.herokuapp.com?version=#{ver}|rainbow> <https://ello.co?version=#{ver}|production>
            """,
            color: "good",
          },
          %{
            title: "Publish",
            text: "Publishing makes this version of webapp active for all users visiting the environment.\n\nPublish with caution.",
            color: "warning",
            callback_id: "publish:webapp",
            actions: [
              %{
                name: "stage1",
                text: "stage1",
                value: ver,
                type: "button"
              },
              %{
                name: "stage2",
                text: "stage2",
                value: ver,
                type: "button"
              },
              %{
                name: "ninja",
                text: "ninja",
                value: ver,
                type: "button"
              },
              %{
                name: "rainbow",
                text: "rainbow",
                value: ver,
                type: "button"
              },
              %{
                name: "production",
                text: "production",
                value: ver,
                style: "danger",
                type: "button",
                confirm: %{
                  title: "Are you sure?",
                  text: "Publishing this version will push it to production for all users.",
                  ok_text: "I got this",
                  dismiss_text: "Nope."
                }
              },
            ]
          }
        ]
      })
    end
  end

  def version_activated("webapp", ver, env, nil) do
    Task.async fn ->
      notify_slack(%{
        attachments: [
          %{
            title: "New version published for all users on #{env}.",
            text:  "Version #{version_link(ver)}",
            color: "good",
          }
        ]
      })
    end
  end

  def version_activated("webapp", ver, env, previous) do
    Task.async fn ->
      notify_slack(%{
        attachments: [
          %{
            title: "New version published for all users on #{env}.",
            text:  "Version #{version_link(ver)}",
            color: "good",
          },
          %{
            title: "Rollback",
            text: "Rollback version #{version_link(ver)} to #{version_link(previous)} on #{env}?",
            color: "warning",
            callback_id: "publish:webapp",
            actions: [
              %{
                name: env,
                text: "Rollback!",
                value: previous,
                style: "danger",
                type: "button",
                confirm: %{
                  title: "Are you sure?",
                  text: "You are about to rollback this version for all users.",
                  ok_text: "I got this",
                  dismiss_text: "Nope.",
                },
              },
            ],
          },
        ],
      })
    end
  end

  def notify_slack(body) do
    case webhook_url() do
      nil -> :ok
      url ->
        HTTPoison.post!(url, Poison.encode!(body), [], [])
    end
  end

  defp webhook_url do
    Application.get_env(:ello_serve, :slack_webhook_url)
  end

  defp compare_link(ver, env) do
    case Ello.Serve.VersionStore.version_history("webapp", env) do
      [_, prev | _] -> "<https://github.com/ello/webapp/compare/#{ver}...#{prev}|#{env}>"
      _             -> ""
    end
  end

  def version_link(ver) do
    nice = String.slice(ver, 0, 7)
    "<https://github.com/ello/webapp/commit/#{ver}|#{nice}>"
  end
end


[SimpleChat] config fixed

[SimpleChat] config fixed

defmodule Config do

  def config do
    [webserver:
      [http_host: "localhost",
       http_port: 8080,
       acceptors: 100,
       ssl: false,
       cacertfile_path: "",
       certfile_path: "",
       keyfile_path: ""
      ],
    ws:
      [ws_port: 8080,
       ws_mod: :Simplechat.Main.Chat
      ],
    localization:
      [default_locale: :en_US,
       use_locales: [:en_US]
      ],
    session:
      [max_age: 1440
      ]
    ]
  end

end

defmodule Weber.DefaultConfig do

  def config do
    [webserver:
      [http_host: "localhost",
       http_port: 8080,
       acceptors: 100,
       ssl: false,
       cacertfile_path: "",
       certfile_path: "",
       keyfile_path: ""
      ],
    ws:
      [ws_port: 8080,
       ws_mod: :Handler
      ],
    localization:
      [default_locale: :en_US,
       use_locales: [:en_US]
      ],
    session:
      [max_age: 1440
      ]
    ]
  end

end


Fix failing test for TabsOrSpaces

Fix failing test for TabsOrSpaces

defmodule Credo.Check.Consistency.TabsOrSpacesTest do
  use Credo.TestHelper

  @described_check Credo.Check.Consistency.TabsOrSpaces

  @with_tabs """
defmodule Credo.Sample do
\t@test_attribute :foo

\tdef foobar(parameter1, parameter2) do
\t\tString.split(parameter1) + parameter2
\tend
end
"""
  @with_spaces """
defmodule Credo.Sample do
  defmodule InlineModule do
    def foobar do
      {:ok} = File.read
    end
  end
end
"""
  @with_spaces2 """
defmodule OtherModule do
  defmacro foo do
    {:ok} = File.read
  end

  defp bar do
    :ok
  end
end
"""

  test "it should report the correct scope" do
    [
      @with_tabs, @with_spaces, @with_spaces2
    ]
    |> Enum.map(&to_source_file/1)
    |> assert_issues(@described_check)
  end

end

defmodule Credo.Check.Consistency.TabsOrSpacesTest do
  use Credo.TestHelper

  @described_check Credo.Check.Consistency.TabsOrSpaces

  @with_tabs """
defmodule Credo.Sample do
\t@test_attribute :foo

\tdef foobar(parameter1, parameter2) do
\t\tString.split(parameter1) + parameter2
\tend
end
"""
  @with_spaces """
defmodule Credo.Sample do
  defmodule InlineModule do
    def foobar do
      {:ok} = File.read
    end
  end
end
"""
  @with_spaces2 """
defmodule OtherModule do
  defmacro foo do
    {:ok} = File.read
  end

  defp bar do
    :ok
  end
end
"""

  test "it should report the correct scope" do
    [
      @with_tabs, @with_spaces, @with_spaces2
    ]
    |> Enum.map(&to_source_file/1)
    |> assert_issue(@described_check)
  end

end


Some organisations are separated by new lines!

So add the new separator and filter out any that are empty

defmodule DataSanitiser.OrganisationUtils do
  @separator_regex ~R{,|;| and | & |/|\\|\n}

  def parse_organisations(organisations_string) do
    organisations_string
    |> String.split(@separator_regex)
    |> Enum.map(&String.trim/1)
    |> Enum.reject(&(&1 == ""))
  end
end

defmodule DataSanitiser.OrganisationUtils do
  @separator_regex ~R{,|;| and | & |/|\\}

  def parse_organisations(organisations_string) do
    organisations_string
    |> String.split(@separator_regex)
    |> Enum.map(&String.trim/1)
  end
end


tweaks

tweaks

defmodule Glasnost.Stage.LookbackBlocks do
  use GenStage
  alias Glasnost.Repo
  require Logger
  alias Ecto.Adapters.SQL
  # @blocks_per_tick 100
  @tick_duration 30_000
  @lookback_max_blocks 201_600

  def start_link(args, options) do
    GenStage.start_link(__MODULE__, args, options)
  end

  def init(%Glasnost.AgentConfig{} = config) do
    Logger.info("LookbackBlocks producer is initializing...")
    {:ok, %{head_block_number: head_block}} = config.client.get_dynamic_global_properties

    config = config
      |> Map.put_new(:starting_block, head_block)
      |> Map.put_new(:current_block, head_block)
    Process.send_after(self(), :next_blocks, @tick_duration)
    {:producer, config, dispatcher: GenStage.BroadcastDispatcher, buffer_size: 100_000}
  end

  def handle_demand(demand, state) do
    {:noreply, [], state}
  end

  def handle_info(:next_blocks, state) do
    import Ecto.Query
    {:ok, %{head_block_number: head_block}} = state.client.get_dynamic_global_properties
    table = Atom.to_string(state.token) <> "_blocks"
    block_schema = Module.concat([state.schema, Block])
    q = ("""
    SELECT generate_series(
        #{head_block},
        #{min_block_to_start(head_block, @lookback_max_blocks)},
        -1)
    EXCEPT (
      SELECT height
      FROM #{table}
    )
    LIMIT 1000
    ;
    """)
    {:ok, %{rows: rows}} = SQL.query(Repo, q)
    tasks = for block_height <- List.flatten(rows) do
      Task.async(
        fn ->
          {:ok, block} = state.client.get_block(block_height)
          block
       end
      )
    end
    blocks = for {_, {:ok, block}} <- Task.yield_many(tasks, 300_000) do
      %{data: block, metadata: %{source: :resync, type: :block}}
    end
    Process.send_after(self(), :next_blocks, @tick_duration)
    Logger.info("Broadcasting missing #{length blocks} block events for #{Atom.to_string(state.token)}")
    {:noreply, blocks, state}
  end

  def min_block_to_start(current_height, blocks) when is_integer(blocks)  do
    delta = current_height - blocks
    if delta < 1, do: 1, else: delta
  end

  def handle_info(msg, state) do
    Logger.info("unexpected msg: #{inspect msg}")
  end
end

defmodule Glasnost.Stage.LookbackBlocks do
  use GenStage
  alias Glasnost.Repo
  require Logger
  alias Ecto.Adapters.SQL
  # @blocks_per_tick 100
  @tick_duration 300_000
  @lookback_max_blocks 201_600

  def start_link(args, options) do
    GenStage.start_link(__MODULE__, args, options)
  end

  def init(%Glasnost.AgentConfig{} = config) do
    Logger.info("LookbackBlocks producer is initializing...")
    {:ok, %{head_block_number: head_block}} = config.client.get_dynamic_global_properties

    config = config
      |> Map.put_new(:starting_block, head_block)
      |> Map.put_new(:current_block, head_block)
    Process.send_after(self(), :next_blocks, 60_000)
    {:producer, config, dispatcher: GenStage.BroadcastDispatcher, buffer_size: 100_000}
  end

  def handle_demand(demand, state) do
    {:noreply, [], state}
  end

  def handle_info(:next_blocks, state) do
    import Ecto.Query
    {:ok, %{head_block_number: head_block}} = state.client.get_dynamic_global_properties
    table = Atom.to_string(state.token) <> "_blocks"
    block_schema = Module.concat([state.schema, Block])
    q = ("""
    SELECT generate_series(
        #{head_block},
        #{min_block_to_start(head_block, @lookback_max_blocks)},
        -1)
    EXCEPT (
      SELECT height
      FROM #{table}
    )
    LIMIT 10000
    ;
    """)
    {:ok, %{rows: rows}} = SQL.query(Repo, q)
    tasks = for block_height <- List.flatten(rows) do
      Task.async(
        fn ->
          {:ok, block} = state.client.get_block(block_height)
          block
       end
      )
    end
    blocks = for {_, {:ok, block}} <- Task.yield_many(tasks, 300_000) do
      %{data: block, metadata: %{source: :resync, type: :block}}
    end
    Logger.info("Broadcasting missing #{length blocks} block events for #{Atom.to_string(state.token)}")
    {:noreply, blocks, state}
  end

  def min_block_to_start(current_height, blocks) when is_integer(blocks)  do
    delta = current_height - blocks
    if delta < 1, do: 1, else: delta
  end

  def handle_info(msg, state) do
    Logger.info("unexpected msg: #{inspect msg}")
  end
end


Product show controller search for slug instead of id

defmodule Api.ProductController do
  use Api.Web, :controller

  alias Api.Product

  plug :scrub_params, "product" when action in [:create, :update]

  def index(conn, _params) do
    products = Repo.all(Product)
    render(conn, "index.json", products: products)
  end

  def create(conn, %{"product" => product_params}) do
    changeset = Product.changeset(%Product{}, product_params)

    case Repo.insert(changeset) do
      {:ok, product} ->
        conn
        |> put_status(:created)
        |> put_resp_header("location", product_path(conn, :show, product))
        |> render("show.json", product: product)
      {:error, changeset} ->
        conn
        |> put_status(:unprocessable_entity)
        |> render(Api.ChangesetView, "error.json", changeset: changeset)
    end
  end

  def show(conn, %{"id" => slug}) do
    query = from p in Product,
      where: p.slug == ^slug,
      select: p
    product = Repo.one!(query)
    render(conn, "show.json", product: product)
  end

  def update(conn, %{"id" => id, "product" => product_params}) do
    product = Repo.get!(Product, id)
    changeset = Product.changeset(product, product_params)

    case Repo.update(changeset) do
      {:ok, product} ->
        render(conn, "show.json", product: product)
      {:error, changeset} ->
        conn
        |> put_status(:unprocessable_entity)
        |> render(Api.ChangesetView, "error.json", changeset: changeset)
    end
  end

  def delete(conn, %{"id" => id}) do
    product = Repo.get!(Product, id)

    # Here we use delete! (with a bang) because we expect
    # it to always work (and if it does not, it will raise).
    Repo.delete!(product)

    send_resp(conn, :no_content, "")
  end
end

defmodule Api.ProductController do
  use Api.Web, :controller

  alias Api.Product

  plug :scrub_params, "product" when action in [:create, :update]

  def index(conn, _params) do
    products = Repo.all(Product)
    render(conn, "index.json", products: products)
  end

  def create(conn, %{"product" => product_params}) do
    changeset = Product.changeset(%Product{}, product_params)

    case Repo.insert(changeset) do
      {:ok, product} ->
        conn
        |> put_status(:created)
        |> put_resp_header("location", product_path(conn, :show, product))
        |> render("show.json", product: product)
      {:error, changeset} ->
        conn
        |> put_status(:unprocessable_entity)
        |> render(Api.ChangesetView, "error.json", changeset: changeset)
    end
  end

  def show(conn, %{"id" => id}) do
    product = Repo.get!(Product, id)
    render(conn, "show.json", product: product)
  end

  def update(conn, %{"id" => id, "product" => product_params}) do
    product = Repo.get!(Product, id)
    changeset = Product.changeset(product, product_params)

    case Repo.update(changeset) do
      {:ok, product} ->
        render(conn, "show.json", product: product)
      {:error, changeset} ->
        conn
        |> put_status(:unprocessable_entity)
        |> render(Api.ChangesetView, "error.json", changeset: changeset)
    end
  end

  def delete(conn, %{"id" => id}) do
    product = Repo.get!(Product, id)

    # Here we use delete! (with a bang) because we expect
    # it to always work (and if it does not, it will raise).
    Repo.delete!(product)

    send_resp(conn, :no_content, "")
  end
end


Fix log typo

Fix log typo

require Logger

defmodule Serverboards.Plugin.Monitor do
  @moduledoc ~S"""
  Monitors changes in the plugins and reloads the registry if necesary.
  """
  use GenServer

  @timeout 5000 # batch changes to until no changes in 1s

  def start_link(options \\ []) do
    if Serverboards.Config.get(:plugins, :watch, true) do
      paths = Serverboards.Plugin.Registry.plugin_paths()
      GenServer.start_link __MODULE__, paths, options
    else
      :ignore
    end
  end

  defp subdirs(dirname) do
    case File.ls(dirname) do
      {:ok, files} ->
        files
          |> Enum.map(&Path.join(dirname,&1))
          |> Enum.filter(&File.dir?(&1))
      {:error, error} ->
        Logger.error("Plugin directory #{dirname} error: #{inspect error}")
        []
    end
  end

  # server
  def init(dirnames) do
    Process.flag(:trap_exit, true)

    cmd = "/usr/bin/inotifywait"
    args = ~w(-q -c -m -e close_write -e moved_to -e create -e delete -e attrib )
    all_subdirs = dirnames |> Enum.flat_map(&subdirs(&1))
    args = args ++ dirnames ++ all_subdirs
    cmdopts = [:stream, :line, :use_stdio, args: args]
    port = Port.open({:spawn_executable, cmd}, cmdopts)
    Port.connect(port, self())

    Logger.info("Plugin monitor ready! #{inspect port} #{inspect(dirnames++all_subdirs)}")

    {:ok, %{ port: port, dirnames: dirnames, expect_exit: false, timeout: :none }}
  end

  def handle_info({_port, {:data,{:eol, data}}}, state) do
    [_, dirname, _actions, filename] = Regex.run(~r/^([^,]*),(.*),([^,]*)$/, List.to_string(data))
    file_path = "#{dirname}/#{filename}"
    Logger.debug("Changes at #{file_path}")

    if state.timeout != :none do
      :timer.cancel(state.timeout)
    end

    {:ok, timeout} = :timer.send_after(@timeout, :reload_plugins)

    {:noreply, %{ state | timeout: timeout }}
  end

  def handle_info(:reload_plugins, state) do
    Logger.info("Detected plugin change. Reloading plugin data.")
    Serverboards.Plugin.Registry.reload_plugins()
    {:noreply, %{state | timeout: :none}}
  end

  def handle_info({:EXIT, _port, :normal}, state) do
    if not state.expect_exit do
      Logger.error("Unexpected inotifywait exit. Check max inotifywait is installed and /proc/sys/fs/inotify/max_user_watches is properly configured")
    end
    {:stop, :normal, state}
  end

  def handle_info(any, state) do
    Logger.debug(inspect any)
    {:noreply, state}
  end

  def terminate(_reason, state) do
    Logger.debug("Terminate plugin monitor #{inspect state.port}")
    Serverboards.IO.Cmd.kill(state.port)
    {:ok}
  end
end

require Logger

defmodule Serverboards.Plugin.Monitor do
  @moduledoc ~S"""
  Monitors changes in the plugins and reloads the registry if necesary.
  """
  use GenServer

  @timeout 5000 # batch changes to until no changes in 1s

  def start_link(options \\ []) do
    if Serverboards.Config.get(:plugins, :watch, true) do
      paths = Serverboards.Plugin.Registry.plugin_paths()
      GenServer.start_link __MODULE__, paths, options
    else
      :ignore
    end
  end

  defp subdirs(dirname) do
    case File.ls(dirname) do
      {:ok, files} ->
        files
          |> Enum.map(&Path.join(dirname,&1))
          |> Enum.filter(&File.dir?(&1))
      {:error, error} ->
        Logger.error("Plugin directory #{dirname} error: #{inspect error}")
        []
    end
  end

  # server
  def init(dirnames) do
    Process.flag(:trap_exit, true)

    cmd = "/usr/bin/inotifywait"
    args = ~w(-q -c -m -e close_write -e moved_to -e create -e delete -e attrib )
    all_subdirs = dirnames |> Enum.flat_map(&subdirs(&1))
    args = args ++ dirnames ++ all_subdirs
    cmdopts = [:stream, :line, :use_stdio, args: args]
    port = Port.open({:spawn_executable, cmd}, cmdopts)
    Port.connect(port, self())

    Logger.info("Plugin monitor ready! #{inspect port} #{inspect(dirnames++all_subdirs)}")

    {:ok, %{ port: port, dirnames: dirnames, expect_exit: false, timeout: :none }}
  end

  def handle_info({_port, {:data,{:eol, data}}}, state) do
    [_, dirname, _actions, filename] = Regex.run(~r/^([^,]*),(.*),([^,]*)$/, List.to_string(data))
    file_path = "#{dirname}#{filename}"
    Logger.debug("Changes at #{file_path}")

    if state.timeout != :none do
      :timer.cancel(state.timeout)
    end

    {:ok, timeout} = :timer.send_after(@timeout, :reload_plugins)

    {:noreply, %{ state | timeout: timeout }}
  end

  def handle_info(:reload_plugins, state) do
    Logger.info("Detected plugin change. Reloading plugin data.")
    Serverboards.Plugin.Registry.reload_plugins()
    {:noreply, %{state | timeout: :none}}
  end

  def handle_info({:EXIT, _port, :normal}, state) do
    if not state.expect_exit do
      Logger.error("Unexpected inotifywait exit. Check max inotifywait is installed and /proc/sys/fs/inotify/max_user_watches is properly configured")
    end
    {:stop, :normal, state}
  end

  def handle_info(any, state) do
    Logger.debug(inspect any)
    {:noreply, state}
  end

  def terminate(_reason, state) do
    Logger.debug("Terminate plugin monitor #{inspect state.port}")
    Serverboards.IO.Cmd.kill(state.port)
    {:ok}
  end
end


fixed incorrect type

fixed incorrect type

defmodule OpenAperture.Manager.Repo.Migrations.EncryptPevValues do
  use Ecto.Migration
  alias OpenAperture.Manager.Repo
  alias OpenAperture.Manager.DB.Models.ProductEnvironmentalVariable
  alias OpenAperture.Manager.Controllers.FormatHelper

  require Repo

  def change do
    alter table(:product_environmental_variables) do
      add :value_keyname, :string, null: true
    end

    ProductEnvironmentalVariable
    |> Repo.all
    |> Enum.map(&updatePEV/1)
  end

  def updatePEV(pev) do
    pev
    |> ProductEnvironmentalVariable.update(%{value: FormatHelper.encrypt_value(pev.value)})
    |> Repo.update!
  end
end

defmodule OpenAperture.Manager.Repo.Migrations.EncryptPevValues do
  use Ecto.Migration
  alias OpenAperture.Manager.Repo
  alias OpenAperture.Manager.DB.Models.ProductEnvironmentalVariable
  alias OpenAperture.Manager.Controllers.FormatHelper

  require Repo

  def change do
    alter table(:product_environmental_variables) do
      add :value_keyname, :boolean, null: true
    end

    ProductEnvironmentalVariable
    |> Repo.all
    |> Enum.map(&updatePEV/1)
  end

  def updatePEV(pev) do
    pev
    |> ProductEnvironmentalVariable.update(%{value: FormatHelper.encrypt_value(pev.value)})
    |> Repo.update
  end
end