From 29508023348c709df5db1fe3ad07e7d38323610f Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Sat, 11 Jan 2025 17:31:45 +1300 Subject: [PATCH] The `Credo` experiment (#1740) This PR adds credo and addresses the most important issues (and some low hanging fruit) raised when using `--strict`. This also adds it to our CI, but it doesn't fail it for now. I've also modified some of the settings: - disabled `{Credo.Check.Readability.ParenthesesOnZeroArityDefs, []}` as I don't know if I agree with this, at the very least we should discuss and decide if we want this enforced. (this check, if enabled, would require us to remove `()` from functions which don't accept any arguments) - disabled `{Credo.Check.Refactor.CondStatements, []}` as we have two 2-condition `cond` statements in `NervesHub.Extensions` which are useful to keep around. If we want to enforce this then we might need to change these until it makes sense switching back to using `cond` for them. - customized `{Credo.Check.Refactor.Nesting, [max_nesting: 3]}`, the default is 2 but we have enough 3 level nesting that I didn't feel like going down that path right now. - customized `Credo.Check.Warning.MissedMetadataKeyInLoggerConfig` to tell it that we do use `:all` in `runtime.exs` - customized `Credo.Check.Readability.ParenthesesOnZeroArityDefs` to require parentheses (the opposite of its default) - enabled `Credo.Check.Warning.LeakyEnvironment` to warn us if we are passing the full environment from the parent process when running system commands. - enabled `Credo.Check.Readability.ImplTrue` as @jjcarstens hates it when `@impl true` is used - and finally, enabled `Credo.Check.Readability.MultiAlias` as the use of `alias NervesHub.{Deployments, Devices, Products, ...}` is a code smell (@jjcarstens can swear to this) We have only `readability` and `software design suggestions` issues to address, or ignore. These primarily consist of: - `Modules should have a @moduledoc tag` - `Nested modules could be aliased at the top of the invoking module.` - and `Found a TODO tag in a comment` I think we should address all of these, but we can instead leave these for another PR. --------- Co-authored-by: Jon Carstens --- .credo.exs | 222 ++++++++++++++++++ .github/workflows/ci.yml | 3 + lib/mix/tasks/assets.build.ex | 5 +- lib/mix/tasks/assets.install.ex | 5 +- lib/mix/tasks/fake.metrics.ex | 4 +- lib/mix/tasks/gen.devices.ex | 2 + lib/nerves_hub/accounts.ex | 21 +- lib/nerves_hub/accounts/org.ex | 6 +- lib/nerves_hub/accounts/org_key.ex | 3 +- lib/nerves_hub/accounts/org_user.ex | 3 +- lib/nerves_hub/accounts/remove_account.ex | 39 +-- lib/nerves_hub/accounts/user.ex | 9 +- lib/nerves_hub/application.ex | 2 +- lib/nerves_hub/archives.ex | 2 +- lib/nerves_hub/archives/archive.ex | 2 +- lib/nerves_hub/deployments.ex | 15 +- lib/nerves_hub/deployments/orchestrator.ex | 6 +- lib/nerves_hub/devices.ex | 120 +--------- lib/nerves_hub/devices/alarms.ex | 4 +- lib/nerves_hub/devices/ca_certificate/csr.ex | 2 +- lib/nerves_hub/devices/device.ex | 2 +- lib/nerves_hub/devices/device_certificate.ex | 9 +- lib/nerves_hub/devices/filtering.ex | 149 ++++++++++++ lib/nerves_hub/devices/inflight_update.ex | 2 +- lib/nerves_hub/devices/metrics.ex | 2 +- lib/nerves_hub/devices/update_payload.ex | 2 +- lib/nerves_hub/extensions.ex | 4 +- lib/nerves_hub/extensions/geo.ex | 2 +- lib/nerves_hub/extensions/health.ex | 2 +- lib/nerves_hub/firmwares.ex | 11 +- .../firmwares/delta_updater/default.ex | 10 +- lib/nerves_hub/firmwares/upload.ex | 3 +- lib/nerves_hub/firmwares/upload/s3.ex | 2 +- lib/nerves_hub/fwup.ex | 7 +- lib/nerves_hub/products.ex | 7 +- lib/nerves_hub/products/product.ex | 2 +- lib/nerves_hub/rate_limit.ex | 4 +- lib/nerves_hub/scripts.ex | 1 + lib/nerves_hub/ssl.ex | 6 +- lib/nerves_hub/telemetry/filtered_sampler.ex | 2 - lib/nerves_hub/types.ex | 6 +- lib/nerves_hub/views/email_view.ex | 4 +- .../workers/clean_device_connection_states.ex | 2 +- lib/nerves_hub/workers/delete_archive.ex | 2 +- lib/nerves_hub/workers/delete_firmware.ex | 2 +- .../workers/device_health_truncation.ex | 7 +- .../workers/expire_inflight_updates.ex | 2 +- .../workers/firmware_delta_builder.ex | 3 +- .../workers/org_audit_log_truncation.ex | 2 +- .../schedule_org_audit_log_truncation.ex | 2 +- lib/nerves_hub_web.ex | 35 +-- lib/nerves_hub_web/channels/device_channel.ex | 13 +- lib/nerves_hub_web/channels/device_socket.ex | 2 +- .../channels/user_console_channel.ex | 4 +- lib/nerves_hub_web/components/navigation.ex | 3 +- .../controllers/account_controller.ex | 3 +- .../api/ca_certificate_controller.ex | 3 +- .../controllers/api/key_controller.ex | 2 +- .../controllers/api/script_controller.ex | 2 +- .../controllers/password_reset_controller.ex | 4 +- .../dynamic_template_renderer.ex | 80 ++++--- lib/nerves_hub_web/errors.ex | 4 +- lib/nerves_hub_web/helpers/authorization.ex | 8 +- lib/nerves_hub_web/live/archives.ex | 3 +- lib/nerves_hub_web/live/dashboard/index.ex | 16 +- lib/nerves_hub_web/live/deployments/show.ex | 3 +- lib/nerves_hub_web/live/devices/index.ex | 3 +- lib/nerves_hub_web/live/devices/settings.ex | 2 +- lib/nerves_hub_web/live/devices/show.ex | 2 +- lib/nerves_hub_web/live/firmware.ex | 3 +- .../live/org/certificate_authorities.ex | 7 +- lib/nerves_hub_web/live/org/products.ex | 2 +- lib/nerves_hub_web/live/org/users.ex | 4 +- lib/nerves_hub_web/live/product/settings.ex | 2 +- .../live/support_scripts/index.ex | 1 + lib/nerves_hub_web/plugs/api/user.ex | 2 +- mix.exs | 1 + mix.lock | 2 + test/nerves_hub/accounts/accounts_test.exs | 6 +- test/nerves_hub/accounts/email_test.exs | 3 +- .../accounts/remove_account_test.exs | 8 +- test/nerves_hub/certificates_test.exs | 3 +- test/nerves_hub/deployments_test.exs | 2 + .../devices/ca_certificate/csr_test.exs | 5 +- test/nerves_hub/devices_test.exs | 4 +- test/nerves_hub/firmwares_test.exs | 18 +- test/nerves_hub/fwup_test.exs | 2 +- test/nerves_hub/products/products_test.exs | 3 +- test/nerves_hub/scripts_test.exs | 2 +- .../workers/device_health_truncation_test.exs | 2 +- ...schedule_org_audit_log_truncation_test.exs | 2 +- .../channels/extensions_channel_test.exs | 2 +- .../channels/websocket_test.exs | 2 +- .../api/ca_certificate_controller_test.exs | 3 +- .../api/deployment_controller_test.exs | 4 +- .../device_certificate_controller_test.exs | 4 +- .../controllers/api/key_controller_test.exs | 2 +- .../api/org_user_controller_test.exs | 2 +- .../password_reset_controller_test.exs | 5 +- .../live/devices/index_test.exs | 4 +- .../nerves_hub_web/live/devices/show_test.exs | 4 +- .../live/org/certificate_authorities_test.exs | 3 +- .../live/support_scripts_test.exs | 2 +- test/nerves_hub_web/plugs/api/user_test.exs | 4 +- test/nerves_hub_web/plugs/org_user_test.exs | 3 +- test/support/archives.ex | 25 +- test/support/fixtures.ex | 10 +- test/support/fwup.ex | 48 ++-- test/support/socket_client.ex | 12 +- test/support/tracker_helper.ex | 2 + 110 files changed, 768 insertions(+), 385 deletions(-) create mode 100644 .credo.exs create mode 100644 lib/nerves_hub/devices/filtering.ex diff --git a/.credo.exs b/.credo.exs new file mode 100644 index 000000000..33ee9d429 --- /dev/null +++ b/.credo.exs @@ -0,0 +1,222 @@ +# This file contains the configuration for Credo and you are probably reading +# this after creating it with `mix credo.gen.config`. +# +# If you find anything wrong or unclear in this file, please report an +# issue on GitHub: https://github.com/rrrene/credo/issues +# +%{ + # + # You can have as many configs as you like in the `configs:` field. + configs: [ + %{ + # + # Run any config using `mix credo -C `. If no config name is given + # "default" is used. + # + name: "default", + # + # These are the files included in the analysis: + files: %{ + # + # You can give explicit globs or simply directories. + # In the latter case `**/*.{ex,exs}` will be used. + # + included: [ + "lib/", + "src/", + "test/", + "web/", + "apps/*/lib/", + "apps/*/src/", + "apps/*/test/", + "apps/*/web/" + ], + excluded: [~r"/_build/", ~r"/deps/", ~r"/node_modules/"] + }, + # + # Load and configure plugins here: + # + plugins: [], + # + # If you create your own checks, you must specify the source files for + # them here, so they can be loaded by Credo before running the analysis. + # + requires: [], + # + # Start enforcing our evolving style guide. + # + strict: true, + # + # To modify the timeout for parsing files, change this value: + # + parse_timeout: 5000, + # + # If you want to use uncolored output by default, you can change `color` + # to `false` below: + # + color: true, + # + # You can customize the parameters of any check by adding a second element + # to the tuple. + # + # To disable a check put `false` as second element: + # + # {Credo.Check.Design.DuplicatedCode, false} + # + checks: %{ + enabled: [ + # + ## Consistency Checks + # + {Credo.Check.Consistency.ExceptionNames, []}, + {Credo.Check.Consistency.LineEndings, []}, + {Credo.Check.Consistency.ParameterPatternMatching, []}, + {Credo.Check.Consistency.SpaceAroundOperators, []}, + {Credo.Check.Consistency.SpaceInParentheses, []}, + {Credo.Check.Consistency.TabsOrSpaces, []}, + + # + ## Design Checks + # + # You can customize the priority of any check + # Priority values are: `low, normal, high, higher` + # + {Credo.Check.Design.AliasUsage, + [priority: :low, if_nested_deeper_than: 2, if_called_more_often_than: 0]}, + {Credo.Check.Design.TagFIXME, []}, + # You can also customize the exit_status of each check. + # If you don't want TODO comments to cause `mix credo` to fail, just + # set this value to 0 (zero). + # + {Credo.Check.Design.TagTODO, [exit_status: 0]}, + + # + ## Readability Checks + # + {Credo.Check.Readability.AliasOrder, []}, + {Credo.Check.Readability.FunctionNames, []}, + {Credo.Check.Readability.ImplTrue, []}, + {Credo.Check.Readability.LargeNumbers, []}, + {Credo.Check.Readability.MaxLineLength, [priority: :low, max_length: 120]}, + {Credo.Check.Readability.ModuleAttributeNames, []}, + {Credo.Check.Readability.ModuleDoc, []}, + {Credo.Check.Readability.ModuleNames, []}, + {Credo.Check.Readability.MultiAlias, []}, + {Credo.Check.Readability.ParenthesesInCondition, []}, + {Credo.Check.Readability.ParenthesesOnZeroArityDefs, [parens: true]}, + {Credo.Check.Readability.PipeIntoAnonymousFunctions, []}, + {Credo.Check.Readability.PredicateFunctionNames, []}, + {Credo.Check.Readability.PreferImplicitTry, []}, + {Credo.Check.Readability.RedundantBlankLines, []}, + {Credo.Check.Readability.Semicolons, []}, + {Credo.Check.Readability.SpaceAfterCommas, []}, + {Credo.Check.Readability.StringSigils, []}, + {Credo.Check.Readability.TrailingBlankLine, []}, + {Credo.Check.Readability.TrailingWhiteSpace, []}, + {Credo.Check.Readability.UnnecessaryAliasExpansion, []}, + {Credo.Check.Readability.VariableNames, []}, + {Credo.Check.Readability.WithSingleClause, []}, + + # + ## Refactoring Opportunities + # + {Credo.Check.Refactor.Apply, []}, + # Disabling this as we use a two 2-statement conds in `NervesHub.Extensions` + # {Credo.Check.Refactor.CondStatements, []}, + {Credo.Check.Refactor.CyclomaticComplexity, []}, + {Credo.Check.Refactor.FilterCount, []}, + {Credo.Check.Refactor.FilterFilter, []}, + {Credo.Check.Refactor.FunctionArity, []}, + {Credo.Check.Refactor.LongQuoteBlocks, []}, + {Credo.Check.Refactor.MapJoin, []}, + {Credo.Check.Refactor.MatchInCondition, []}, + {Credo.Check.Refactor.NegatedConditionsInUnless, []}, + {Credo.Check.Refactor.NegatedConditionsWithElse, []}, + # Customized to 3, as we use a lot of 3 level nesting + {Credo.Check.Refactor.Nesting, [max_nesting: 3]}, + {Credo.Check.Refactor.RedundantWithClauseResult, []}, + {Credo.Check.Refactor.RejectReject, []}, + {Credo.Check.Refactor.UnlessWithElse, []}, + {Credo.Check.Refactor.WithClauses, []}, + + # + ## Warnings + # + {Credo.Check.Warning.ApplicationConfigInModuleAttribute, []}, + {Credo.Check.Warning.BoolOperationOnSameValues, []}, + {Credo.Check.Warning.Dbg, []}, + {Credo.Check.Warning.ExpensiveEmptyEnumCheck, []}, + {Credo.Check.Warning.IExPry, []}, + {Credo.Check.Warning.IoInspect, []}, + {Credo.Check.Warning.LeakyEnvironment, []}, + # we use `:all` in `runtime.exs`, credo wasn't picking this up + {Credo.Check.Warning.MissedMetadataKeyInLoggerConfig, + [ + metadata_keys: :all + ]}, + {Credo.Check.Warning.OperationOnSameValues, []}, + {Credo.Check.Warning.OperationWithConstantResult, []}, + {Credo.Check.Warning.RaiseInsideRescue, []}, + {Credo.Check.Warning.SpecWithStruct, []}, + {Credo.Check.Warning.UnsafeExec, []}, + {Credo.Check.Warning.UnusedEnumOperation, []}, + {Credo.Check.Warning.UnusedFileOperation, []}, + {Credo.Check.Warning.UnusedKeywordOperation, []}, + {Credo.Check.Warning.UnusedListOperation, []}, + {Credo.Check.Warning.UnusedPathOperation, []}, + {Credo.Check.Warning.UnusedRegexOperation, []}, + {Credo.Check.Warning.UnusedStringOperation, []}, + {Credo.Check.Warning.UnusedTupleOperation, []}, + {Credo.Check.Warning.WrongTestFileExtension, []} + ], + disabled: [ + # + # Checks scheduled for next check update (opt-in for now) + {Credo.Check.Refactor.UtcNowTruncate, []}, + + # + # Controversial and experimental checks (opt-in, just move the check to `:enabled` + # and be sure to use `mix credo --strict` to see low priority checks) + # + {Credo.Check.Consistency.MultiAliasImportRequireUse, []}, + {Credo.Check.Consistency.UnusedVariableNames, []}, + {Credo.Check.Design.DuplicatedCode, []}, + {Credo.Check.Design.SkipTestWithoutComment, []}, + {Credo.Check.Readability.AliasAs, []}, + {Credo.Check.Readability.BlockPipe, []}, + {Credo.Check.Readability.NestedFunctionCalls, []}, + {Credo.Check.Readability.OneArityFunctionInPipe, []}, + {Credo.Check.Readability.OnePipePerLine, []}, + {Credo.Check.Readability.SeparateAliasRequire, []}, + {Credo.Check.Readability.SingleFunctionToBlockPipe, []}, + {Credo.Check.Readability.SinglePipe, []}, + {Credo.Check.Readability.Specs, []}, + {Credo.Check.Readability.StrictModuleLayout, []}, + {Credo.Check.Readability.WithCustomTaggedTuple, []}, + {Credo.Check.Refactor.ABCSize, []}, + {Credo.Check.Refactor.AppendSingleItem, []}, + {Credo.Check.Refactor.DoubleBooleanNegation, []}, + {Credo.Check.Refactor.FilterReject, []}, + {Credo.Check.Refactor.IoPuts, []}, + {Credo.Check.Refactor.MapMap, []}, + {Credo.Check.Refactor.ModuleDependencies, []}, + {Credo.Check.Refactor.NegatedIsNil, []}, + {Credo.Check.Refactor.PassAsyncInTestCases, []}, + {Credo.Check.Refactor.PipeChainStart, []}, + {Credo.Check.Refactor.RejectFilter, []}, + {Credo.Check.Refactor.VariableRebinding, []}, + {Credo.Check.Warning.LazyLogging, []}, + {Credo.Check.Warning.MapGetUnsafePass, []}, + {Credo.Check.Warning.MixEnv, []}, + {Credo.Check.Warning.UnsafeToAtom, []} + + # {Credo.Check.Refactor.MapInto, []}, + + # + # Custom checks can be created using `mix credo.gen.check`. + # + ] + } + } + ] +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d817ddc54..c64f6baf3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,6 +92,9 @@ jobs: - name: Check for unused dependencies run: mix deps.unlock --unused + - name: Run Credo (won't fail the build) + run: mix credo --mute-exit-status + - name: DB Setup run: mix ecto.migrate.reset diff --git a/lib/mix/tasks/assets.build.ex b/lib/mix/tasks/assets.build.ex index f6d313dfb..1af9eaaaf 100644 --- a/lib/mix/tasks/assets.build.ex +++ b/lib/mix/tasks/assets.build.ex @@ -1,4 +1,6 @@ defmodule Mix.Tasks.Assets.Build do + @moduledoc false + use Mix.Task @shortdoc "Build web assets" @@ -10,7 +12,8 @@ defmodule Mix.Tasks.Assets.Build do ["run", "deploy"], cd: @assets, stderr_to_stdout: true, - into: IO.stream(:stdio, :line) + into: IO.stream(:stdio, :line), + env: [] ) end end diff --git a/lib/mix/tasks/assets.install.ex b/lib/mix/tasks/assets.install.ex index 6e629255a..3624fced1 100644 --- a/lib/mix/tasks/assets.install.ex +++ b/lib/mix/tasks/assets.install.ex @@ -1,4 +1,6 @@ defmodule Mix.Tasks.Assets.Install do + @moduledoc false + use Mix.Task @shortdoc "Install web assets" @@ -10,7 +12,8 @@ defmodule Mix.Tasks.Assets.Install do ["install"], cd: @assets, stderr_to_stdout: true, - into: IO.stream(:stdio, :line) + into: IO.stream(:stdio, :line), + env: [] ) end end diff --git a/lib/mix/tasks/fake.metrics.ex b/lib/mix/tasks/fake.metrics.ex index 087ef2773..5c7b07090 100644 --- a/lib/mix/tasks/fake.metrics.ex +++ b/lib/mix/tasks/fake.metrics.ex @@ -1,10 +1,12 @@ if Mix.env() == :dev do defmodule Mix.Tasks.Fake.Metrics do + @moduledoc false + use Mix.Task - alias NervesHub.Repo alias NervesHub.Devices alias NervesHub.Devices.DeviceMetric + alias NervesHub.Repo @shortdoc "Create randomized metrics for device" @requirements ["app.start"] diff --git a/lib/mix/tasks/gen.devices.ex b/lib/mix/tasks/gen.devices.ex index c45a5903a..a652530d9 100644 --- a/lib/mix/tasks/gen.devices.ex +++ b/lib/mix/tasks/gen.devices.ex @@ -1,4 +1,6 @@ defmodule Mix.Tasks.Gen.Devices do + @moduledoc false + use Mix.Task @shortdoc "Generate a mass of devices" diff --git a/lib/nerves_hub/accounts.ex b/lib/nerves_hub/accounts.ex index 8e030587d..b01ffbbca 100644 --- a/lib/nerves_hub/accounts.ex +++ b/lib/nerves_hub/accounts.ex @@ -1,19 +1,18 @@ defmodule NervesHub.Accounts do import Ecto.Query - alias Ecto.{Changeset, Multi} + alias Ecto.Changeset + alias Ecto.Multi alias Ecto.UUID - alias NervesHub.Accounts.{ - Org, - User, - UserToken, - Invite, - OrgKey, - OrgUser, - OrgMetric, - RemoveAccount - } + alias NervesHub.Accounts.Invite + alias NervesHub.Accounts.Org + alias NervesHub.Accounts.OrgKey + alias NervesHub.Accounts.OrgMetric + alias NervesHub.Accounts.OrgUser + alias NervesHub.Accounts.RemoveAccount + alias NervesHub.Accounts.User + alias NervesHub.Accounts.UserToken alias NervesHub.Repo diff --git a/lib/nerves_hub/accounts/org.ex b/lib/nerves_hub/accounts/org.ex index 981f2bf5a..dcd6c8021 100644 --- a/lib/nerves_hub/accounts/org.ex +++ b/lib/nerves_hub/accounts/org.ex @@ -4,8 +4,10 @@ defmodule NervesHub.Accounts.Org do import Ecto.Changeset import Ecto.Query - alias NervesHub.Accounts.{OrgKey, OrgUser} - alias NervesHub.Devices.{Device, CACertificate} + alias NervesHub.Accounts.OrgKey + alias NervesHub.Accounts.OrgUser + alias NervesHub.Devices.CACertificate + alias NervesHub.Devices.Device alias NervesHub.Products.Product alias NervesHub.Repo alias __MODULE__ diff --git a/lib/nerves_hub/accounts/org_key.ex b/lib/nerves_hub/accounts/org_key.ex index e5c39bf84..b9cb5da30 100644 --- a/lib/nerves_hub/accounts/org_key.ex +++ b/lib/nerves_hub/accounts/org_key.ex @@ -3,7 +3,8 @@ defmodule NervesHub.Accounts.OrgKey do import Ecto.Changeset - alias NervesHub.Accounts.{Org, User} + alias NervesHub.Accounts.Org + alias NervesHub.Accounts.User alias NervesHub.Firmwares.Firmware alias __MODULE__ diff --git a/lib/nerves_hub/accounts/org_user.ex b/lib/nerves_hub/accounts/org_user.ex index d17f2457e..1a89c3fd5 100644 --- a/lib/nerves_hub/accounts/org_user.ex +++ b/lib/nerves_hub/accounts/org_user.ex @@ -3,7 +3,8 @@ defmodule NervesHub.Accounts.OrgUser do import Ecto.Query - alias NervesHub.Accounts.{User, Org} + alias NervesHub.Accounts.Org + alias NervesHub.Accounts.User @type t :: %__MODULE__{} diff --git a/lib/nerves_hub/accounts/remove_account.ex b/lib/nerves_hub/accounts/remove_account.ex index dc5a2c0af..10d02d39e 100644 --- a/lib/nerves_hub/accounts/remove_account.ex +++ b/lib/nerves_hub/accounts/remove_account.ex @@ -1,21 +1,26 @@ defmodule NervesHub.Accounts.RemoveAccount do - import Ecto.{Query, Changeset} + import Ecto.Query + import Ecto.Changeset + alias Ecto.Multi - alias NervesHub.{ - Accounts, - Firmwares, - Deployments.Deployment, - Products, - Repo, - OrgKey, - Devices - } - - alias Firmwares.{Firmware, FirmwareDelta, FirmwareTransfer} - alias Accounts.{Org, OrgUser, OrgKey, Invite, User, OrgMetric} - alias Devices.{DeviceCertificate, Device, CACertificate} - alias Products.Product + alias NervesHub.Accounts.Invite + alias NervesHub.Accounts.Org + alias NervesHub.Accounts.OrgKey + alias NervesHub.Accounts.OrgMetric + alias NervesHub.Accounts.OrgUser + alias NervesHub.Accounts.User + alias NervesHub.Deployments.Deployment + alias NervesHub.Devices.CACertificate + alias NervesHub.Devices.Device + alias NervesHub.Devices.DeviceCertificate + alias NervesHub.Firmwares + alias NervesHub.Firmwares.Firmware + alias NervesHub.Firmwares.FirmwareDelta + alias NervesHub.Firmwares.FirmwareTransfer + alias NervesHub.Products.Product + + alias NervesHub.Repo def remove_account(user_id) do Multi.new() @@ -39,7 +44,7 @@ defmodule NervesHub.Accounts.RemoveAccount do |> Repo.transaction() end - defp query_org_users do + defp query_org_users() do from( org_user in OrgUser, join: user in assoc(org_user, :user), @@ -82,7 +87,7 @@ defmodule NervesHub.Accounts.RemoveAccount do end) end - defp truncated_utc_now do + defp truncated_utc_now() do DateTime.truncate(DateTime.utc_now(), :second) end diff --git a/lib/nerves_hub/accounts/user.ex b/lib/nerves_hub/accounts/user.ex index ae58297d7..b65b1305e 100644 --- a/lib/nerves_hub/accounts/user.ex +++ b/lib/nerves_hub/accounts/user.ex @@ -4,13 +4,16 @@ defmodule NervesHub.Accounts.User do import Ecto.Changeset import Ecto.Query - alias NervesHub.Accounts.{OrgUser, UserToken} + alias NervesHub.Accounts.OrgUser + alias NervesHub.Accounts.UserToken + alias NervesHub.Repo alias Ecto.Changeset - alias __MODULE__ alias Ecto.UUID + alias __MODULE__ + @type t :: %__MODULE__{} @password_min_length 8 @@ -177,7 +180,7 @@ defmodule NervesHub.Accounts.User do The time length that a password reset token is valid. Passed to Timex.shift, so it just has to be a keyword list with :minutes, :hours, etc. """ - def password_reset_window do + def password_reset_window() do [hours: 8] end diff --git a/lib/nerves_hub/application.ex b/lib/nerves_hub/application.ex index ed2d83783..3ee3e1fab 100644 --- a/lib/nerves_hub/application.ex +++ b/lib/nerves_hub/application.ex @@ -4,7 +4,7 @@ defmodule NervesHub.Application do require Logger def start(_type, _args) do - case System.cmd("fwup", ["--version"]) do + case System.cmd("fwup", ["--version"], env: []) do {_, 0} -> Logger.debug("fwup was found") diff --git a/lib/nerves_hub/archives.ex b/lib/nerves_hub/archives.ex index 318ad002d..71682f302 100644 --- a/lib/nerves_hub/archives.ex +++ b/lib/nerves_hub/archives.ex @@ -100,7 +100,7 @@ defmodule NervesHub.Archives do defp validate_signature(org, file_path) do signed_key = Enum.find(org.org_keys, fn %{key: key} -> - case System.cmd("fwup", ["--verify", "--public-key", key, "-i", file_path]) do + case System.cmd("fwup", ["--verify", "--public-key", key, "-i", file_path], env: []) do {_, 0} -> true diff --git a/lib/nerves_hub/archives/archive.ex b/lib/nerves_hub/archives/archive.ex index cb74aeff9..4be6b6ea0 100644 --- a/lib/nerves_hub/archives/archive.ex +++ b/lib/nerves_hub/archives/archive.ex @@ -4,8 +4,8 @@ defmodule NervesHub.Archives.Archive do import Ecto.Changeset alias NervesHub.Accounts.OrgKey - alias NervesHub.Products.Product alias NervesHub.Fwup.Metadata + alias NervesHub.Products.Product @type t :: %__MODULE__{ architecture: String.t(), diff --git a/lib/nerves_hub/deployments.ex b/lib/nerves_hub/deployments.ex index e640e2975..3518e035e 100644 --- a/lib/nerves_hub/deployments.ex +++ b/lib/nerves_hub/deployments.ex @@ -10,7 +10,10 @@ defmodule NervesHub.Deployments do alias NervesHub.Devices alias NervesHub.Devices.Device alias NervesHub.Products.Product + alias NervesHub.Workers.FirmwareDeltaBuilder + alias NervesHub.Repo + alias Ecto.Changeset @spec all() :: [Deployment.t()] @@ -214,7 +217,7 @@ defmodule NervesHub.Deployments do NervesHub.Devices.get_device_firmware_for_delta_generation_by_deployment(deployment.id) |> Enum.uniq() |> Enum.each(fn {source_id, target_id} -> - NervesHub.Workers.FirmwareDeltaBuilder.start(source_id, target_id) + FirmwareDeltaBuilder.start(source_id, target_id) end) end @@ -294,8 +297,7 @@ defmodule NervesHub.Deployments do |> where([dev], fragment("d0.firmware_metadata ->> 'platform'") == ^platform) |> where([dev], fragment("?::jsonb->'tags' <@ to_jsonb(?::text[])", ^conditions, dev.tags)) |> Repo.all() - |> Enum.filter(&version_match?(&1, %{conditions: conditions})) - |> Enum.count() + |> Enum.count(&version_match?(&1, %{conditions: conditions})) end # Check that a device version matches for a deployment's conditions @@ -404,9 +406,14 @@ defmodule NervesHub.Deployments do ) end + @spec preload_firmware_and_archive(Deployment.t()) :: Deployment.t() + def preload_firmware_and_archive(deployment) do + %Deployment{} = Repo.preload(deployment, [:archive, :firmware]) + end + @spec preload_with_firmware_and_archive(Device.t(), boolean()) :: Device.t() def preload_with_firmware_and_archive(device, force \\ false) do - Repo.preload(device, [deployment: [:archive, :firmware]], force: force) + %Device{} = Repo.preload(device, [deployment: [:archive, :firmware]], force: force) end @doc """ diff --git a/lib/nerves_hub/deployments/orchestrator.ex b/lib/nerves_hub/deployments/orchestrator.ex index 601e202d5..866fa22e1 100644 --- a/lib/nerves_hub/deployments/orchestrator.ex +++ b/lib/nerves_hub/deployments/orchestrator.ex @@ -13,11 +13,13 @@ defmodule NervesHub.Deployments.Orchestrator do require Logger - alias NervesHub.Devices - alias NervesHub.Devices.Device alias NervesHub.Deployments alias NervesHub.Deployments.Deployment + alias NervesHub.Devices + alias NervesHub.Devices.Device + alias NervesHub.Repo + alias Phoenix.PubSub alias Phoenix.Socket.Broadcast diff --git a/lib/nerves_hub/devices.ex b/lib/nerves_hub/devices.ex index 60fb1bdab..23304634c 100644 --- a/lib/nerves_hub/devices.ex +++ b/lib/nerves_hub/devices.ex @@ -12,14 +12,13 @@ defmodule NervesHub.Devices do alias NervesHub.Deployments.Deployment alias NervesHub.Deployments.Orchestrator alias NervesHub.Devices.CACertificate - alias NervesHub.Devices.Alarms alias NervesHub.Devices.Connections alias NervesHub.Devices.Device alias NervesHub.Devices.DeviceCertificate alias NervesHub.Devices.DeviceHealth - alias NervesHub.Devices.DeviceMetric - alias NervesHub.Devices.SharedSecretAuth + alias NervesHub.Devices.Filtering alias NervesHub.Devices.InflightUpdate + alias NervesHub.Devices.SharedSecretAuth alias NervesHub.Devices.UpdatePayload alias NervesHub.Extensions alias NervesHub.Firmwares @@ -91,7 +90,7 @@ defmodule NervesHub.Devices do |> join(:left, [d, o, p, dp], f in assoc(dp, :firmware)) |> Repo.exclude_deleted() |> order_by(^sort_devices(sorting)) - |> filtering(filters) + |> Filtering.build_filters(filters) |> preload([d, o, p, dp, f], org: o, product: p, deployment: {dp, firmware: f}) |> Connections.preload_latest_connection() |> Flop.run(flop) @@ -128,7 +127,7 @@ defmodule NervesHub.Devices do |> where([d], d.product_id == ^product_id) |> Connections.preload_latest_connection() |> Repo.exclude_deleted() - |> filtering(filters) + |> Filtering.build_filters(filters) |> order_by(^sort_devices(sorting)) |> Flop.run(flop) |> then(fn {entries, meta} -> @@ -180,7 +179,7 @@ defmodule NervesHub.Devices do query |> Repo.exclude_deleted() - |> filtering(filters) + |> Filtering.build_filters(filters) |> Repo.all() |> Enum.reduce(%{max_cpu: 0, max_memory_percent: 0, max_load_15: 0}, fn health, acc -> case Enum.at(health, 1) do @@ -212,115 +211,6 @@ defmodule NervesHub.Devices do defp sort_devices(sort), do: sort - defp filtering(query, filters) do - Enum.reduce(filters, query, fn {key, value}, query -> - case {key, value} do - # Filter values are empty strings as default, - # they should be ignored. - {_, ""} -> - query - - {:alarm, value} -> - where(query, [d], d.id in subquery(Alarms.query_devices_with_alarm(value))) - - {:alarm_status, "with"} -> - where(query, [d], d.id in subquery(Alarms.query_devices_with_alarms())) - - {:alarm_status, "without"} -> - where(query, [d], d.id not in subquery(Alarms.query_devices_with_alarms())) - - {:connection, "not_seen"} -> - where(query, [d], d.status == :registered) - - {:connection, value} -> - where( - query, - [d], - d.id in subquery(Connections.query_devices_with_connection_status(value)) - ) - - {:connection_type, value} -> - where(query, [d], ^value in d.connection_types) - - {:firmware_version, value} -> - where(query, [d], d.firmware_metadata["version"] == ^value) - - {:platform, "Unknown"} -> - where(query, [d], is_nil(d.firmware_metadata["platform"])) - - {:platform, value} -> - where(query, [d], d.firmware_metadata["platform"] == ^value) - - {:updates, "enabled"} -> - where(query, [d], d.updates_enabled == true) - - {:updates, "penalty-box"} -> - where(query, [d], d.updates_blocked_until > fragment("now() at time zone 'utc'")) - - {:updates, "disabled"} -> - where(query, [d], d.updates_enabled == false) - - {:device_id, value} -> - where(query, [d], ilike(d.identifier, ^"%#{value}%")) - - {:tag, value} -> - case NervesHub.Types.Tag.cast(value) do - {:ok, tags} -> - Enum.reduce(tags, query, fn tag, query -> - where( - query, - [d], - fragment("string_array_to_string(?, ' ', ' ') ILIKE ?", d.tags, ^"%#{tag}%") - ) - end) - - {:error, _} -> - query - end - - {:has_no_tags, value} -> - if value do - where(query, [d], fragment("array_length(?, 1) = 0 or ? IS NULL", d.tags, d.tags)) - else - query - end - - {:metrics_value, _value} -> - filter_on_metric(query, filters) - - # Ignore any undefined filter. - # This will prevent error 500 responses on deprecated saved bookmarks etc. - _ -> - query - end - end) - end - - defp filter_on_metric( - query, - %{metrics_key: key, metrics_operator: operator, metrics_value: value} - ) - when key != "" do - {value_as_float, _} = Float.parse(value) - - query - |> join(:inner, [d], m in DeviceMetric, on: d.id == m.device_id) - |> where([_, m], m.inserted_at == subquery(latest_metric_for_key(key))) - |> where([d, m], m.key == ^key) - |> gt_or_lt(value_as_float, operator) - end - - defp filter_on_metric(query, _), do: query - - defp latest_metric_for_key(key) do - DeviceMetric - |> select([dm], max(dm.inserted_at)) - |> where([dm], dm.key == ^key) - end - - defp gt_or_lt(query, value, "gt"), do: where(query, [_, dm], dm.value > ^value) - defp gt_or_lt(query, value, "lt"), do: where(query, [_, dm], dm.value < ^value) - def get_device_count_by_org_id(org_id) do q = from( diff --git a/lib/nerves_hub/devices/alarms.ex b/lib/nerves_hub/devices/alarms.ex index e51de4082..6d0a1fd1d 100644 --- a/lib/nerves_hub/devices/alarms.ex +++ b/lib/nerves_hub/devices/alarms.ex @@ -1,10 +1,12 @@ defmodule NervesHub.Devices.Alarms do import Ecto.Query - alias NervesHub.Repo + alias NervesHub.Devices alias NervesHub.Devices.Device alias NervesHub.Devices.DeviceHealth + alias NervesHub.Repo + @doc """ Selects device id:s for devices that has alarm(s) in it's latest health record. Used when filtering devices. diff --git a/lib/nerves_hub/devices/ca_certificate/csr.ex b/lib/nerves_hub/devices/ca_certificate/csr.ex index 3feee81a9..d9cef48ee 100644 --- a/lib/nerves_hub/devices/ca_certificate/csr.ex +++ b/lib/nerves_hub/devices/ca_certificate/csr.ex @@ -12,7 +12,7 @@ defmodule NervesHub.Devices.CACertificate.CSR do after a user creates the CSR. """ @spec generate_code() :: csr_code() - def generate_code do + def generate_code() do :crypto.strong_rand_bytes(32) |> Base.encode16() end diff --git a/lib/nerves_hub/devices/device.ex b/lib/nerves_hub/devices/device.ex index 0643a5335..cb17b1399 100644 --- a/lib/nerves_hub/devices/device.ex +++ b/lib/nerves_hub/devices/device.ex @@ -4,10 +4,10 @@ defmodule NervesHub.Devices.Device do import Ecto.Changeset alias NervesHub.Accounts.Org + alias NervesHub.Deployments.Deployment alias NervesHub.Devices.DeviceCertificate alias NervesHub.Devices.DeviceConnection alias NervesHub.Devices.DeviceMetric - alias NervesHub.Deployments.Deployment alias NervesHub.Extensions.DeviceExtensionsSetting alias NervesHub.Firmwares.FirmwareMetadata alias NervesHub.Products.Product diff --git a/lib/nerves_hub/devices/device_certificate.ex b/lib/nerves_hub/devices/device_certificate.ex index db9d4027b..18fc829ae 100644 --- a/lib/nerves_hub/devices/device_certificate.ex +++ b/lib/nerves_hub/devices/device_certificate.ex @@ -1,11 +1,16 @@ defmodule NervesHub.Devices.DeviceCertificate do use Ecto.Schema + import Ecto.Changeset import Ecto.Query, only: [from: 2] alias NervesHub.Accounts.Org - alias NervesHub.{Certificate, Devices, Repo} - alias NervesHub.Devices.{Device, DeviceCertificate} + alias NervesHub.Certificate + alias NervesHub.Devices + alias NervesHub.Devices.Device + alias NervesHub.Devices.DeviceCertificate + + alias NervesHub.Repo @type t :: %__MODULE__{} diff --git a/lib/nerves_hub/devices/filtering.ex b/lib/nerves_hub/devices/filtering.ex new file mode 100644 index 000000000..c422534af --- /dev/null +++ b/lib/nerves_hub/devices/filtering.ex @@ -0,0 +1,149 @@ +defmodule NervesHub.Devices.Filtering do + @moduledoc """ + Encapsulates all device filtering logic + """ + + import Ecto.Query + + alias NervesHub.Devices.Alarms + alias NervesHub.Devices.Connections + alias NervesHub.Devices.DeviceMetric + alias NervesHub.Types.Tag + + @spec build_filters(Ecto.Query.t(), %{optional(atom) => String.t()}) :: Ecto.Query.t() + def build_filters(query, filters) do + Enum.reduce(filters, query, fn {key, value}, query -> + filter(query, filters, key, value) + end) + end + + @spec filter(Ecto.Query.t(), %{optional(atom) => String.t()}, atom, String.t()) :: + Ecto.Query.t() + def filter(query, filters, key, value) + + # Filter values are empty strings as default, + # they should be ignored. + def filter(query, _filters, _key, "") do + query + end + + def filter(query, _filters, :alarm, value) do + where(query, [d], d.id in subquery(Alarms.query_devices_with_alarm(value))) + end + + def filter(query, _filters, :alarm_status, value) do + case value do + "with" -> where(query, [d], d.id in subquery(Alarms.query_devices_with_alarms())) + "without" -> where(query, [d], d.id not in subquery(Alarms.query_devices_with_alarms())) + _ -> query + end + end + + def filter(query, _filters, :connection, value) do + if value == "not_seen" do + where(query, [d], d.status == :registered) + else + where( + query, + [d], + d.id in subquery(Connections.query_devices_with_connection_status(value)) + ) + end + end + + def filter(query, _filters, :connection_type, value) do + where(query, [d], ^value in d.connection_types) + end + + def filter(query, _filters, :firmware_version, value) do + where(query, [d], d.firmware_metadata["version"] == ^value) + end + + def filter(query, _filters, :platform, value) do + if value == "Unknown" do + where(query, [d], is_nil(d.firmware_metadata["platform"])) + else + where(query, [d], d.firmware_metadata["platform"] == ^value) + end + end + + def filter(query, _filters, :updates, value) do + case value do + "enabled" -> + where(query, [d], d.updates_enabled == true) + + "penalty-box" -> + where(query, [d], d.updates_blocked_until > fragment("now() at time zone 'utc'")) + + "disabled" -> + where(query, [d], d.updates_enabled == false) + end + end + + def filter(query, _filters, :device_id, value) do + where(query, [d], ilike(d.identifier, ^"%#{value}%")) + end + + def filter(query, _filters, :tag, value) do + build_tag_filter(query, value) + end + + def filter(query, _filters, :has_no_tags, value) do + if value do + where(query, [d], fragment("array_length(?, 1) = 0 or ? IS NULL", d.tags, d.tags)) + else + query + end + end + + def filter(query, filters, :metrics_key, _value) do + filter_on_metric(query, filters) + end + + # Ignore any undefined filter. + # This will prevent error 500 responses on deprecated saved bookmarks etc. + def filter(query, _filters, _key, _value) do + query + end + + defp build_tag_filter(query, value) do + case Tag.cast(value) do + {:ok, tags} -> + Enum.reduce(tags, query, fn tag, query -> + where( + query, + [d], + fragment("string_array_to_string(?, ' ', ' ') ILIKE ?", d.tags, ^"%#{tag}%") + ) + end) + + {:error, _} -> + query + end + end + + defp filter_on_metric( + query, + %{metrics_key: key, metrics_operator: operator, metrics_value: value} + ) + when key != "" do + {value_as_float, _} = Float.parse(value) + + query + |> join(:inner, [d], m in DeviceMetric, on: d.id == m.device_id) + |> where([_, m], m.inserted_at == subquery(latest_metric_for_key(key))) + |> where([d, m], m.key == ^key) + |> gt_or_lt(value_as_float, operator) + end + + defp filter_on_metric(query, _), do: query + + defp latest_metric_for_key(key) do + DeviceMetric + |> select([dm], max(dm.inserted_at)) + |> where([dm], dm.key == ^key) + end + + defp gt_or_lt(query, value, "gt"), do: where(query, [_, dm], dm.value > ^value) + defp gt_or_lt(query, value, "lt"), do: where(query, [_, dm], dm.value < ^value) +end diff --git a/lib/nerves_hub/devices/inflight_update.ex b/lib/nerves_hub/devices/inflight_update.ex index 55854f329..bd90aece1 100644 --- a/lib/nerves_hub/devices/inflight_update.ex +++ b/lib/nerves_hub/devices/inflight_update.ex @@ -3,9 +3,9 @@ defmodule NervesHub.Devices.InflightUpdate do import Ecto.Changeset + alias NervesHub.Deployments.Deployment alias NervesHub.Devices.Device alias NervesHub.Devices.InflightUpdate - alias NervesHub.Deployments.Deployment alias NervesHub.Firmwares.Firmware @required_params [:device_id, :deployment_id, :firmware_id, :firmware_uuid, :expires_at] diff --git a/lib/nerves_hub/devices/metrics.ex b/lib/nerves_hub/devices/metrics.ex index a2053043b..4c68082ee 100644 --- a/lib/nerves_hub/devices/metrics.ex +++ b/lib/nerves_hub/devices/metrics.ex @@ -18,7 +18,7 @@ defmodule NervesHub.Devices.Metrics do "mem_used_percent" ] - def default_metrics, do: @default_metrics + def default_metrics(), do: @default_metrics @doc """ Get all metrics for device diff --git a/lib/nerves_hub/devices/update_payload.ex b/lib/nerves_hub/devices/update_payload.ex index 19b421885..0c4948693 100644 --- a/lib/nerves_hub/devices/update_payload.ex +++ b/lib/nerves_hub/devices/update_payload.ex @@ -3,8 +3,8 @@ defmodule NervesHub.Devices.UpdatePayload do This struct represents the payload that gets dispatched to devices """ - alias NervesHub.Firmwares.FirmwareMetadata alias NervesHub.Deployments.Deployment + alias NervesHub.Firmwares.FirmwareMetadata @derive {Jason.Encoder, only: [ diff --git a/lib/nerves_hub/extensions.ex b/lib/nerves_hub/extensions.ex index b3b9ce2e5..c2ebc5b0d 100644 --- a/lib/nerves_hub/extensions.ex +++ b/lib/nerves_hub/extensions.ex @@ -38,9 +38,7 @@ defmodule NervesHub.Extensions do Get list of supported extensions as atoms with descriptive text. """ @spec list() :: [:geo | :health, ...] - def list do - @supported_extensions - end + def list(), do: @supported_extensions @spec module(extension()) :: NervesHub.Extensions.Geo | NervesHub.Extensions.Health def module(:health), do: NervesHub.Extensions.Health diff --git a/lib/nerves_hub/extensions/geo.ex b/lib/nerves_hub/extensions/geo.ex index 93ab4e176..6bdb1dc8b 100644 --- a/lib/nerves_hub/extensions/geo.ex +++ b/lib/nerves_hub/extensions/geo.ex @@ -4,7 +4,7 @@ defmodule NervesHub.Extensions.Geo do alias NervesHub.Devices @impl NervesHub.Extensions - def description do + def description() do """ Reporting of GeoIP information or custom geo-location information sources you've set up for your device. diff --git a/lib/nerves_hub/extensions/health.ex b/lib/nerves_hub/extensions/health.ex index 4161b0abd..4b7591196 100644 --- a/lib/nerves_hub/extensions/health.ex +++ b/lib/nerves_hub/extensions/health.ex @@ -8,7 +8,7 @@ defmodule NervesHub.Extensions.Health do require Logger @impl NervesHub.Extensions - def description do + def description() do """ Reporting of fundamental device metrics, metadata, alarms and more. Also supports custom metrics. Alarms require an alarm handler to be set. diff --git a/lib/nerves_hub/firmwares.ex b/lib/nerves_hub/firmwares.ex index 7e4c4010b..95c049767 100644 --- a/lib/nerves_hub/firmwares.ex +++ b/lib/nerves_hub/firmwares.ex @@ -2,16 +2,19 @@ defmodule NervesHub.Firmwares do import Ecto.Query alias Ecto.Changeset - alias NervesHub.Accounts.OrgKey + alias NervesHub.Accounts.Org + alias NervesHub.Accounts.OrgKey alias NervesHub.Devices.Device alias NervesHub.Firmwares.Firmware - alias NervesHub.Firmwares.FirmwareMetadata alias NervesHub.Firmwares.FirmwareDelta + alias NervesHub.Firmwares.FirmwareMetadata alias NervesHub.Firmwares.FirmwareTransfer alias NervesHub.Fwup alias NervesHub.Products alias NervesHub.Products.Product + alias NervesHub.Workers.DeleteFirmware + alias NervesHub.Repo require Logger @@ -157,7 +160,7 @@ defmodule NervesHub.Firmwares do do_delete_from_s3 = fn -> firmware.upload_metadata - |> NervesHub.Workers.DeleteFirmware.new() + |> DeleteFirmware.new() |> Oban.insert() end @@ -180,7 +183,7 @@ defmodule NervesHub.Firmwares do def verify_signature(filepath, keys) when is_binary(filepath) do signed_key = Enum.find(keys, fn %{key: key} -> - case System.cmd("fwup", ["--verify", "--public-key", key, "-i", filepath]) do + case System.cmd("fwup", ["--verify", "--public-key", key, "-i", filepath], env: []) do {_, 0} -> true diff --git a/lib/nerves_hub/firmwares/delta_updater/default.ex b/lib/nerves_hub/firmwares/delta_updater/default.ex index 5c0b4542d..15c941d92 100644 --- a/lib/nerves_hub/firmwares/delta_updater/default.ex +++ b/lib/nerves_hub/firmwares/delta_updater/default.ex @@ -48,7 +48,7 @@ defmodule NervesHub.Firmwares.DeltaUpdater.Default do @impl NervesHub.Firmwares.DeltaUpdater def delta_updatable?(file_path) do - {meta, 0} = System.cmd("unzip", ["-qqp", file_path, "meta.conf"]) + {meta, 0} = System.cmd("unzip", ["-qqp", file_path, "meta.conf"], env: []) (meta =~ "delta-source-raw-offset" && meta =~ "delta-source-raw-count") or (meta =~ "delta-source-fat-offset" && meta =~ "delta-source-fat-path") @@ -65,8 +65,8 @@ defmodule NervesHub.Firmwares.DeltaUpdater.Default do _ = File.mkdir_p(target_work_dir) _ = File.mkdir_p(output_work_dir) - {_, 0} = System.cmd("unzip", ["-qq", source_path, "-d", source_work_dir]) - {_, 0} = System.cmd("unzip", ["-qq", target_path, "-d", target_work_dir]) + {_, 0} = System.cmd("unzip", ["-qq", source_path, "-d", source_work_dir], env: []) + {_, 0} = System.cmd("unzip", ["-qq", target_path, "-d", target_work_dir], env: []) _ = for absolute <- Path.wildcard(target_work_dir <> "/**"), not File.dir?(absolute) do @@ -97,7 +97,7 @@ defmodule NervesHub.Firmwares.DeltaUpdater.Default do output_path ] - {_, 0} = System.cmd("xdelta3", args, stderr_to_stdout: true) + {_, 0} = System.cmd("xdelta3", args, stderr_to_stdout: true, env: []) {:error, :enoent} -> File.cp!(target_filepath, output_path) @@ -129,7 +129,7 @@ defmodule NervesHub.Firmwares.DeltaUpdater.Default do paths -> args = ["-r", "-qq", output | Enum.map(paths, &Path.relative_to(&1, workdir))] - {_, 0} = System.cmd("zip", args, cd: workdir) + {_, 0} = System.cmd("zip", args, cd: workdir, env: []) :ok end diff --git a/lib/nerves_hub/firmwares/upload.ex b/lib/nerves_hub/firmwares/upload.ex index f50f30a6c..93e400241 100644 --- a/lib/nerves_hub/firmwares/upload.ex +++ b/lib/nerves_hub/firmwares/upload.ex @@ -6,8 +6,9 @@ defmodule NervesHub.Firmwares.Upload do @typedoc "Metadata about the file upload." @type upload_metadata :: map() - alias NervesHub.Firmwares.{Firmware, FirmwareDelta} alias NervesHub.Accounts.Org + alias NervesHub.Firmwares.Firmware + alias NervesHub.Firmwares.FirmwareDelta @doc """ Called to upload a file to where it needs to live. diff --git a/lib/nerves_hub/firmwares/upload/s3.ex b/lib/nerves_hub/firmwares/upload/s3.ex index 636d1e544..8331cb167 100644 --- a/lib/nerves_hub/firmwares/upload/s3.ex +++ b/lib/nerves_hub/firmwares/upload/s3.ex @@ -55,7 +55,7 @@ defmodule NervesHub.Firmwares.Upload.S3 do %{"s3_key" => Path.join([key_prefix(), Integer.to_string(org_id), filename])} end - def bucket do + def bucket() do Application.get_env(:nerves_hub, __MODULE__)[:bucket] end diff --git a/lib/nerves_hub/fwup.ex b/lib/nerves_hub/fwup.ex index 7e11bcde1..10aa13072 100644 --- a/lib/nerves_hub/fwup.ex +++ b/lib/nerves_hub/fwup.ex @@ -50,14 +50,13 @@ defmodule NervesHub.Fwup do | {:error, :invalid_fwup_file | :invalid_metadata} def metadata(file_path) do with {:ok, metadata} <- get_metadata(file_path), - parsed_metadata <- parse_metadata(metadata), - {:ok, metadata_struct} <- transform_to_struct(parsed_metadata) do - {:ok, metadata_struct} + parsed_metadata <- parse_metadata(metadata) do + transform_to_struct(parsed_metadata) end end defp get_metadata(filepath) do - case System.cmd("fwup", ["-m", "-i", filepath]) do + case System.cmd("fwup", ["-m", "-i", filepath], env: []) do {metadata, 0} -> {:ok, metadata} diff --git a/lib/nerves_hub/products.ex b/lib/nerves_hub/products.ex index d8ff4467a..6bd461567 100644 --- a/lib/nerves_hub/products.ex +++ b/lib/nerves_hub/products.ex @@ -9,17 +9,18 @@ defmodule NervesHub.Products do alias NervesHub.Accounts.Org alias NervesHub.Accounts.OrgUser + alias NervesHub.Accounts.User alias NervesHub.Extensions alias NervesHub.Products.Product alias NervesHub.Products.SharedSecretAuth - alias NervesHub.Accounts.User + alias NervesHub.Workers.FirmwareDeltaBuilder alias NimbleCSV.RFC4180, as: CSV @csv_certs_sep "\n\n" @csv_header ["identifier", "description", "tags", "product", "org", "certificates"] - def __csv_header__, do: @csv_header + def __csv_header__(), do: @csv_header @spec get_products_by_user_and_org(User.t(), Org.t()) :: [Product.t()] def get_products_by_user_and_org(%User{id: user_id}, %Org{id: org_id}) do @@ -136,7 +137,7 @@ defmodule NervesHub.Products do NervesHub.Devices.get_device_firmware_for_delta_generation_by_product(product.id) |> Enum.uniq() |> Enum.each(fn {source_id, target_id} -> - NervesHub.Workers.FirmwareDeltaBuilder.start(source_id, target_id) + FirmwareDeltaBuilder.start(source_id, target_id) end) end diff --git a/lib/nerves_hub/products/product.ex b/lib/nerves_hub/products/product.ex index 3a2c38ee1..107712872 100644 --- a/lib/nerves_hub/products/product.ex +++ b/lib/nerves_hub/products/product.ex @@ -5,12 +5,12 @@ defmodule NervesHub.Products.Product do alias NervesHub.Accounts.Org alias NervesHub.Archives.Archive alias NervesHub.Deployments.Deployment - alias NervesHub.Scripts.Script alias NervesHub.Devices.CACertificate alias NervesHub.Devices.Device alias NervesHub.Extensions.ProductExtensionsSetting alias NervesHub.Firmwares.Firmware alias NervesHub.Products.SharedSecretAuth + alias NervesHub.Scripts.Script @required_params [:name, :org_id] @optional_params [:delta_updatable] diff --git a/lib/nerves_hub/rate_limit.ex b/lib/nerves_hub/rate_limit.ex index 9f9b79344..bf67c5eaf 100644 --- a/lib/nerves_hub/rate_limit.ex +++ b/lib/nerves_hub/rate_limit.ex @@ -28,7 +28,7 @@ defmodule NervesHub.RateLimit do GenServer.start_link(__MODULE__, []) end - @impl true + @impl GenServer def init(_) do state = %{ ets_key: :nerves_hub_rate_limit @@ -48,7 +48,7 @@ defmodule NervesHub.RateLimit do {:ok, state} end - @impl true + @impl GenServer def handle_info(:prune, state) do minute_ago = DateTime.utc_now() diff --git a/lib/nerves_hub/scripts.ex b/lib/nerves_hub/scripts.ex index c45336453..06d24b30f 100644 --- a/lib/nerves_hub/scripts.ex +++ b/lib/nerves_hub/scripts.ex @@ -2,6 +2,7 @@ defmodule NervesHub.Scripts do import Ecto.Query alias NervesHub.Scripts.Script + alias NervesHub.Repo def all_by_product(product) do diff --git a/lib/nerves_hub/ssl.ex b/lib/nerves_hub/ssl.ex index 8124d7299..179e5c5a7 100644 --- a/lib/nerves_hub/ssl.ex +++ b/lib/nerves_hub/ssl.ex @@ -1,6 +1,10 @@ defmodule NervesHub.SSL do - alias NervesHub.Devices + @moduledoc """ + Custom SSL peer cert verification for Devices. + """ + alias NervesHub.Certificate + alias NervesHub.Devices @type pkix_path_validation_reason :: :cert_expired diff --git a/lib/nerves_hub/telemetry/filtered_sampler.ex b/lib/nerves_hub/telemetry/filtered_sampler.ex index 063f6fce2..e519ec98a 100644 --- a/lib/nerves_hub/telemetry/filtered_sampler.ex +++ b/lib/nerves_hub/telemetry/filtered_sampler.ex @@ -1,8 +1,6 @@ defmodule NervesHub.Telemetry.FilteredSampler do # Inspired by https://arathunku.com/b/2024/notes-on-adding-opentelemetry-to-an-elixir-app/ - # TODO: Add ratio sampling support - require OpenTelemetry.Tracer, as: Tracer require Logger diff --git a/lib/nerves_hub/types.ex b/lib/nerves_hub/types.ex index 5e87ac9fc..89f64d073 100644 --- a/lib/nerves_hub/types.ex +++ b/lib/nerves_hub/types.ex @@ -2,7 +2,7 @@ defmodule NervesHub.Types do defmodule Tag do @behaviour Ecto.Type - def type, do: {:array, :string} + def type(), do: {:array, :string} def embed_as(_), do: :self @@ -33,7 +33,7 @@ defmodule NervesHub.Types do defmodule Resource do @behaviour Ecto.Type - def type, do: :string + def type(), do: :string def embed_as(_), do: :self @@ -69,7 +69,7 @@ defmodule NervesHub.Types do def load(resource), do: {:ok, String.to_existing_atom(resource)} - defp allowed_resources do + defp allowed_resources() do [ "Elixir.NervesHub.Accounts.Org", "Elixir.NervesHub.Accounts.User", diff --git a/lib/nerves_hub/views/email_view.ex b/lib/nerves_hub/views/email_view.ex index 11d474460..82f9f1f8d 100644 --- a/lib/nerves_hub/views/email_view.ex +++ b/lib/nerves_hub/views/email_view.ex @@ -5,7 +5,7 @@ defmodule NervesHub.EmailView do import Phoenix.HTML - def base_url do + def base_url() do config = Application.get_env(:nerves_hub, NervesHubWeb.Endpoint) port = @@ -21,7 +21,7 @@ defmodule NervesHub.EmailView do Standard closing words. """ - def closing do + def closing() do """ #{closing_message()} diff --git a/lib/nerves_hub/workers/clean_device_connection_states.ex b/lib/nerves_hub/workers/clean_device_connection_states.ex index 3008237a8..94accc644 100644 --- a/lib/nerves_hub/workers/clean_device_connection_states.ex +++ b/lib/nerves_hub/workers/clean_device_connection_states.ex @@ -6,7 +6,7 @@ defmodule NervesHub.Workers.CleanDeviceConnectionStates do alias NervesHub.Devices alias NervesHub.Devices.Connections - @impl true + @impl Oban.Worker def perform(_) do Devices.clean_connection_states() Connections.clean_stale_connections() diff --git a/lib/nerves_hub/workers/delete_archive.ex b/lib/nerves_hub/workers/delete_archive.ex index 377443367..eab121065 100644 --- a/lib/nerves_hub/workers/delete_archive.ex +++ b/lib/nerves_hub/workers/delete_archive.ex @@ -3,7 +3,7 @@ defmodule NervesHub.Workers.DeleteArchive do max_attempts: 5, queue: :delete_archive - @impl true + @impl Oban.Worker def perform(%Oban.Job{args: %{"archive_path" => path}}) do backend = Application.fetch_env!(:nerves_hub, NervesHub.Uploads)[:backend] diff --git a/lib/nerves_hub/workers/delete_firmware.ex b/lib/nerves_hub/workers/delete_firmware.ex index 07b712365..5137cdca0 100644 --- a/lib/nerves_hub/workers/delete_firmware.ex +++ b/lib/nerves_hub/workers/delete_firmware.ex @@ -3,7 +3,7 @@ defmodule NervesHub.Workers.DeleteFirmware do max_attempts: 5, queue: :delete_firmware - @impl true + @impl Oban.Worker def perform(%Oban.Job{args: args}) do uploader = Application.fetch_env!(:nerves_hub, :firmware_upload) uploader.delete_file(args) diff --git a/lib/nerves_hub/workers/device_health_truncation.ex b/lib/nerves_hub/workers/device_health_truncation.ex index 381614ef5..daa375d65 100644 --- a/lib/nerves_hub/workers/device_health_truncation.ex +++ b/lib/nerves_hub/workers/device_health_truncation.ex @@ -10,10 +10,13 @@ defmodule NervesHub.Workers.DeviceHealthTruncation do max_attempts: 1, queue: :truncate + alias NervesHub.Devices + alias NervesHub.Devices.Metrics + @impl Oban.Worker def perform(_) do - {:ok, _} = NervesHub.Devices.truncate_device_health() - {:ok, _} = NervesHub.Devices.Metrics.truncate_device_metrics() + {:ok, _} = Devices.truncate_device_health() + {:ok, _} = Metrics.truncate_device_metrics() :ok end diff --git a/lib/nerves_hub/workers/expire_inflight_updates.ex b/lib/nerves_hub/workers/expire_inflight_updates.ex index d81846ed4..cddf6a3e4 100644 --- a/lib/nerves_hub/workers/expire_inflight_updates.ex +++ b/lib/nerves_hub/workers/expire_inflight_updates.ex @@ -13,7 +13,7 @@ defmodule NervesHub.Workers.ExpireInflightUpdates do alias NervesHub.Devices - @impl true + @impl Oban.Worker def perform(_) do {count, _} = Devices.delete_expired_inflight_updates() diff --git a/lib/nerves_hub/workers/firmware_delta_builder.ex b/lib/nerves_hub/workers/firmware_delta_builder.ex index 2331b944c..6572eda2c 100644 --- a/lib/nerves_hub/workers/firmware_delta_builder.ex +++ b/lib/nerves_hub/workers/firmware_delta_builder.ex @@ -7,7 +7,8 @@ defmodule NervesHub.Workers.FirmwareDeltaBuilder do states: [:available, :scheduled, :executing] ] - alias NervesHub.{Deployments, Firmwares} + alias NervesHub.Deployments + alias NervesHub.Firmwares @impl Oban.Worker def perform(%Oban.Job{args: %{"source_id" => source_id, "target_id" => target_id}}) do diff --git a/lib/nerves_hub/workers/org_audit_log_truncation.ex b/lib/nerves_hub/workers/org_audit_log_truncation.ex index 809a64b86..b4b45f9d6 100644 --- a/lib/nerves_hub/workers/org_audit_log_truncation.ex +++ b/lib/nerves_hub/workers/org_audit_log_truncation.ex @@ -3,7 +3,7 @@ defmodule NervesHub.Workers.OrgAuditLogTruncation do max_attempts: 5, queue: :truncate - @impl true + @impl Oban.Worker def perform(%Oban.Job{args: %{"org_id" => id, "days_to_keep" => days_to_keep}}) do {:ok, _} = NervesHub.AuditLogs.truncate(id, days_to_keep) diff --git a/lib/nerves_hub/workers/schedule_org_audit_log_truncation.ex b/lib/nerves_hub/workers/schedule_org_audit_log_truncation.ex index 8adf2d612..695de1a75 100644 --- a/lib/nerves_hub/workers/schedule_org_audit_log_truncation.ex +++ b/lib/nerves_hub/workers/schedule_org_audit_log_truncation.ex @@ -5,7 +5,7 @@ defmodule NervesHub.Workers.ScheduleOrgAuditLogTruncation do alias NervesHub.Accounts alias NervesHub.Workers.OrgAuditLogTruncation - @impl true + @impl Oban.Worker def perform(_) do if enabled?() do orgs = Accounts.get_orgs() diff --git a/lib/nerves_hub_web.ex b/lib/nerves_hub_web.ex index f277647f6..1270b9e32 100644 --- a/lib/nerves_hub_web.ex +++ b/lib/nerves_hub_web.ex @@ -17,16 +17,16 @@ defmodule NervesHubWeb do and import those modules here. """ - def static_paths, do: ~w(assets fonts images favicon.ico robots.txt) + def static_paths(), do: ~w(assets fonts images favicon.ico robots.txt) - def plug do + def plug() do quote do import Plug.Conn import Phoenix.Controller end end - def controller do + def controller() do quote do use Phoenix.Controller, namespace: NervesHubWeb use Gettext, backend: NervesHubWeb.Gettext @@ -50,7 +50,7 @@ defmodule NervesHubWeb do end end - def api_controller do + def api_controller() do quote do use Phoenix.Controller, namespace: NervesHubWeb use Gettext, backend: NervesHubWeb.Gettext @@ -71,7 +71,7 @@ defmodule NervesHubWeb do end end - def updated_live_view do + def updated_live_view() do quote do use NervesHubWeb.LiveView, layout: {NervesHubWeb.LayoutView, :live}, @@ -82,7 +82,7 @@ defmodule NervesHubWeb do # HTML escaping functionality import Phoenix.HTML - import NervesHub.Helpers.Authorization + import NervesHubWeb.Helpers.Authorization # Shortcut for generating JS commands alias Phoenix.LiveView.JS @@ -110,7 +110,7 @@ defmodule NervesHubWeb do end end - def verified_routes do + def verified_routes() do quote do use Phoenix.VerifiedRoutes, endpoint: NervesHubWeb.Endpoint, @@ -119,7 +119,7 @@ defmodule NervesHubWeb do end end - def live_component do + def live_component() do quote do use Phoenix.LiveComponent @@ -127,7 +127,7 @@ defmodule NervesHubWeb do end end - def html do + def html() do quote do use Phoenix.Component @@ -140,7 +140,7 @@ defmodule NervesHubWeb do end end - defp html_helpers do + defp html_helpers() do quote do # HTML escaping functionality import Phoenix.HTML @@ -156,13 +156,14 @@ defmodule NervesHubWeb do end end - def view do + def view() do quote do use Phoenix.View, root: "lib/nerves_hub_web/templates", namespace: NervesHubWeb - alias NervesHubWeb.{DeviceLive, Endpoint} + alias NervesHubWeb.DeviceLive + alias NervesHubWeb.Endpoint alias NervesHubWeb.Components.Navigation @@ -182,7 +183,7 @@ defmodule NervesHubWeb do end end - def api_view do + def api_view() do quote do use Phoenix.View, root: "lib/nerves_hub_web/templates", @@ -205,7 +206,7 @@ defmodule NervesHubWeb do end end - def component do + def component() do quote do use Phoenix.Component @@ -214,7 +215,7 @@ defmodule NervesHubWeb do end end - def router do + def router() do quote do use Phoenix.Router import Plug.Conn @@ -223,14 +224,14 @@ defmodule NervesHubWeb do end end - def channel do + def channel() do quote do use Phoenix.Channel use Gettext, backend: NervesHubWeb.Gettext end end - defp view_helpers do + defp view_helpers() do quote do # Use all HTML functionality (forms, tags, etc) use Phoenix.HTML diff --git a/lib/nerves_hub_web/channels/device_channel.ex b/lib/nerves_hub_web/channels/device_channel.ex index b677edc29..29ed69a45 100644 --- a/lib/nerves_hub_web/channels/device_channel.ex +++ b/lib/nerves_hub_web/channels/device_channel.ex @@ -22,11 +22,12 @@ defmodule NervesHubWeb.DeviceChannel do @decorate with_span("Channels.DeviceChannel.join") def join("device", params, %{assigns: %{device: device}} = socket) do - with {:ok, device} <- update_metadata(device, params) do - send(self(), {:after_join, params}) + case update_metadata(device, params) do + {:ok, device} -> + send(self(), {:after_join, params}) + + {:ok, assign(socket, :device, device)} - {:ok, assign(socket, :device, device)} - else err -> Logger.warning("[DeviceChannel] failure to connect - #{inspect(err)}") {:error, %{error: "could not connect"}} @@ -65,7 +66,7 @@ defmodule NervesHubWeb.DeviceChannel do # Request device extension capabilities if possible # Earlier versions of nerves_hub_link don't have a fallback for unknown messages, # so check version before requesting extensions - if is_safe_to_request_extensions?(socket.assigns.device_api_version), + if safe_to_request_extensions?(socket.assigns.device_api_version), do: push(socket, "extensions:get", %{}), else: Templates.audit_unsupported_api_version(device) @@ -484,5 +485,5 @@ defmodule NervesHubWeb.DeviceChannel do socket end - defp is_safe_to_request_extensions?(version), do: Version.match?(version, ">= 2.2.0") + defp safe_to_request_extensions?(version), do: Version.match?(version, ">= 2.2.0") end diff --git a/lib/nerves_hub_web/channels/device_socket.ex b/lib/nerves_hub_web/channels/device_socket.ex index d11c1a2d5..d8bdebdea 100644 --- a/lib/nerves_hub_web/channels/device_socket.ex +++ b/lib/nerves_hub_web/channels/device_socket.ex @@ -5,8 +5,8 @@ defmodule NervesHubWeb.DeviceSocket do require Logger alias NervesHub.Devices - alias NervesHub.Devices.Device alias NervesHub.Devices.Connections + alias NervesHub.Devices.Device alias NervesHub.Devices.DeviceConnection alias NervesHub.Products alias NervesHub.Tracker diff --git a/lib/nerves_hub_web/channels/user_console_channel.ex b/lib/nerves_hub_web/channels/user_console_channel.ex index 0f9c7c806..ceaf566ac 100644 --- a/lib/nerves_hub_web/channels/user_console_channel.ex +++ b/lib/nerves_hub_web/channels/user_console_channel.ex @@ -2,7 +2,9 @@ defmodule NervesHubWeb.UserConsoleChannel do use NervesHubWeb, :channel alias NervesHub.Accounts - alias NervesHub.Helpers.Authorization + + alias NervesHubWeb.Helpers.Authorization + alias Phoenix.Socket.Broadcast def join("user:console:" <> device_id, _, socket) do diff --git a/lib/nerves_hub_web/components/navigation.ex b/lib/nerves_hub_web/components/navigation.ex index 2afdfb51d..ec4c8ba94 100644 --- a/lib/nerves_hub_web/components/navigation.ex +++ b/lib/nerves_hub_web/components/navigation.ex @@ -1,6 +1,7 @@ defmodule NervesHubWeb.Components.Navigation do use NervesHubWeb, :component + alias NervesHub.Accounts.User alias NervesHub.Devices alias NervesHub.Devices.Alarms alias NervesHub.Products.Product @@ -297,7 +298,7 @@ defmodule NervesHubWeb.Components.Navigation do href: ~p"/org/#{assigns.org.name}" } ] ++ - if assigns.org_user.role in NervesHub.Accounts.User.role_or_higher(:manage) do + if assigns.org_user.role in User.role_or_higher(:manage) do [ %{ title: "Signing Keys", diff --git a/lib/nerves_hub_web/controllers/account_controller.ex b/lib/nerves_hub_web/controllers/account_controller.ex index d8b4fde72..b62ab8df0 100644 --- a/lib/nerves_hub_web/controllers/account_controller.ex +++ b/lib/nerves_hub_web/controllers/account_controller.ex @@ -3,7 +3,8 @@ defmodule NervesHubWeb.AccountController do alias Ecto.Changeset alias NervesHub.Accounts - alias NervesHub.Accounts.{User, SwooshEmail} + alias NervesHub.Accounts.SwooshEmail + alias NervesHub.Accounts.User alias NervesHub.SwooshMailer plug(:registrations_allowed when action in [:new, :create]) diff --git a/lib/nerves_hub_web/controllers/api/ca_certificate_controller.ex b/lib/nerves_hub_web/controllers/api/ca_certificate_controller.ex index 86dc6752a..0807684af 100644 --- a/lib/nerves_hub_web/controllers/api/ca_certificate_controller.ex +++ b/lib/nerves_hub_web/controllers/api/ca_certificate_controller.ex @@ -1,7 +1,8 @@ defmodule NervesHubWeb.API.CACertificateController do use NervesHubWeb, :api_controller - alias NervesHub.{Devices, Certificate} + alias NervesHub.Certificate + alias NervesHub.Devices action_fallback(NervesHubWeb.API.FallbackController) diff --git a/lib/nerves_hub_web/controllers/api/key_controller.ex b/lib/nerves_hub_web/controllers/api/key_controller.ex index e0aa1c8e3..55a3fa8f2 100644 --- a/lib/nerves_hub_web/controllers/api/key_controller.ex +++ b/lib/nerves_hub_web/controllers/api/key_controller.ex @@ -2,7 +2,7 @@ defmodule NervesHubWeb.API.KeyController do use NervesHubWeb, :api_controller alias NervesHub.Accounts - alias NervesHub.Accounts.{OrgKey} + alias NervesHub.Accounts.OrgKey action_fallback(NervesHubWeb.API.FallbackController) diff --git a/lib/nerves_hub_web/controllers/api/script_controller.ex b/lib/nerves_hub_web/controllers/api/script_controller.ex index 1a5a95bd6..7cb642ad7 100644 --- a/lib/nerves_hub_web/controllers/api/script_controller.ex +++ b/lib/nerves_hub_web/controllers/api/script_controller.ex @@ -2,8 +2,8 @@ defmodule NervesHubWeb.API.ScriptController do use NervesHubWeb, :controller alias NervesHub.Accounts - alias NervesHub.Scripts alias NervesHub.Devices + alias NervesHub.Scripts def index(conn, %{"identifier" => identifier}) do %{user: user} = conn.assigns diff --git a/lib/nerves_hub_web/controllers/password_reset_controller.ex b/lib/nerves_hub_web/controllers/password_reset_controller.ex index d4625c1f0..8a0fa8c53 100644 --- a/lib/nerves_hub_web/controllers/password_reset_controller.ex +++ b/lib/nerves_hub_web/controllers/password_reset_controller.ex @@ -1,10 +1,10 @@ defmodule NervesHubWeb.PasswordResetController do use NervesHubWeb, :controller + alias NervesHub.Accounts + alias NervesHub.Accounts.PasswordReset alias NervesHub.Accounts.SwooshEmail alias NervesHub.Accounts.User - alias NervesHub.Accounts.PasswordReset - alias NervesHub.Accounts alias NervesHub.SwooshMailer alias Ecto.Changeset diff --git a/lib/nerves_hub_web/dynamic_template_renderer.ex b/lib/nerves_hub_web/dynamic_template_renderer.ex index 44f4f9309..c57d0efad 100644 --- a/lib/nerves_hub_web/dynamic_template_renderer.ex +++ b/lib/nerves_hub_web/dynamic_template_renderer.ex @@ -26,44 +26,7 @@ defmodule NervesHubWeb.DynamicTemplateRenderer do :ok {false, [template]} -> - ext = template |> Path.extname() |> String.trim_leading(".") |> String.to_atom() - engine = Map.fetch!(Phoenix.Template.engines(), ext) - ast = engine.compile(template, filename) - - new_filename = template_filename(module, "-new") - new_templates = Phoenix.Template.find_all(root, new_filename) - - case new_templates do - [new_template] -> - Logger.info("Found New UI page: #{new_template}") - - new_ext = - new_template |> Path.extname() |> String.trim_leading(".") |> String.to_atom() - - new_engine = Map.fetch!(Phoenix.Template.engines(), new_ext) - new_ast = new_engine.compile(new_template, filename) - - quote do - @file unquote(template) - @external_resource unquote(template) - def render(var!(assigns)) when is_map(var!(assigns)) do - if Application.get_env(:nerves_hub, :new_ui) && var!(assigns)[:new_ui] do - unquote(new_ast) - else - unquote(ast) - end - end - end - - _ -> - quote do - @file unquote(template) - @external_resource unquote(template) - def render(var!(assigns)) when is_map(var!(assigns)) do - unquote(ast) - end - end - end + custom_multi_template_processing(template, filename, module, root) {false, [_ | _]} -> IO.warn( @@ -82,6 +45,47 @@ defmodule NervesHubWeb.DynamicTemplateRenderer do end end + defp custom_multi_template_processing(template, filename, module, root) do + ext = template |> Path.extname() |> String.trim_leading(".") |> String.to_atom() + engine = Map.fetch!(Phoenix.Template.engines(), ext) + ast = engine.compile(template, filename) + + new_filename = template_filename(module, "-new") + new_templates = Phoenix.Template.find_all(root, new_filename) + + case new_templates do + [new_template] -> + Logger.info("Found New UI page: #{new_template}") + + new_ext = + new_template |> Path.extname() |> String.trim_leading(".") |> String.to_atom() + + new_engine = Map.fetch!(Phoenix.Template.engines(), new_ext) + new_ast = new_engine.compile(new_template, filename) + + quote do + @file unquote(template) + @external_resource unquote(template) + def render(var!(assigns)) when is_map(var!(assigns)) do + if Application.get_env(:nerves_hub, :new_ui) && var!(assigns)[:new_ui] do + unquote(new_ast) + else + unquote(ast) + end + end + end + + _ -> + quote do + @file unquote(template) + @external_resource unquote(template) + def render(var!(assigns)) when is_map(var!(assigns)) do + unquote(ast) + end + end + end + end + defp template_filename(module, suffix \\ "") do module |> Module.split() diff --git a/lib/nerves_hub_web/errors.ex b/lib/nerves_hub_web/errors.ex index 97ddd0245..e6926e063 100644 --- a/lib/nerves_hub_web/errors.ex +++ b/lib/nerves_hub_web/errors.ex @@ -2,6 +2,6 @@ defmodule NervesHubWeb.NotFoundError do defexception message: "not found", plug_status: 404 end -defmodule NervesHubWeb.Unauthorized do - defexception message: "forbidden", plug_status: 401 +defmodule NervesHubWeb.UnauthorizedError do + defexception message: "unauthorized", plug_status: 401 end diff --git a/lib/nerves_hub_web/helpers/authorization.ex b/lib/nerves_hub_web/helpers/authorization.ex index 2bb10e39a..3969fc319 100644 --- a/lib/nerves_hub_web/helpers/authorization.ex +++ b/lib/nerves_hub_web/helpers/authorization.ex @@ -1,13 +1,9 @@ -defmodule NervesHub.Errors.Unauthorized do - defexception message: "unauthorized", plug_status: 401 -end - -defmodule NervesHub.Helpers.Authorization do +defmodule NervesHubWeb.Helpers.Authorization do alias NervesHub.Accounts.OrgUser alias NervesHub.Accounts.User def authorized!(org_user, permission) do - authorized?(org_user, permission) || raise NervesHub.Errors.Unauthorized + authorized?(org_user, permission) || raise NervesHubWeb.UnauthorizedError end def authorized?(:"organization:update", %OrgUser{role: ur}), do: role_check(:admin, ur) diff --git a/lib/nerves_hub_web/live/archives.ex b/lib/nerves_hub_web/live/archives.ex index b0fdd48c1..e757e9e33 100644 --- a/lib/nerves_hub_web/live/archives.ex +++ b/lib/nerves_hub_web/live/archives.ex @@ -1,7 +1,8 @@ defmodule NervesHubWeb.Live.Archives do use NervesHubWeb, :updated_live_view - alias NervesHub.{Accounts, Archives} + alias NervesHub.Accounts + alias NervesHub.Archives embed_templates("archive_templates/*") diff --git a/lib/nerves_hub_web/live/dashboard/index.ex b/lib/nerves_hub_web/live/dashboard/index.ex index 289d71a2e..a3b3d17fd 100644 --- a/lib/nerves_hub_web/live/dashboard/index.ex +++ b/lib/nerves_hub_web/live/dashboard/index.ex @@ -1,13 +1,13 @@ defmodule NervesHubWeb.Live.Dashboard.Index do use NervesHubWeb, :updated_live_view + alias NervesHub.Deployments alias NervesHub.Devices alias NervesHub.Devices.Device - alias NervesHub.Deployments alias Phoenix.Socket.Broadcast - @default_refresh 53000 + @default_refresh 53_000 @delay 500 @impl Phoenix.LiveView @@ -67,9 +67,7 @@ defmodule NervesHubWeb.Live.Dashboard.Index do defp subscribe_to_devices(socket, devices) do if connected?(socket) do Enum.each(devices, fn device -> - if not subscribed?(device) do - socket.endpoint.subscribe("device:#{device.identifier}:internal") - end + maybe_subscribe(socket, device) end) end end @@ -80,6 +78,12 @@ defmodule NervesHubWeb.Live.Dashboard.Index do ]) > 0 end + defp maybe_subscribe(socket, device) do + if not subscribed?(device) do + socket.endpoint.subscribe("device:#{device.identifier}:internal") + end + end + defp update_devices_and_markers(%{assigns: %{org: org, product: product}} = socket) do t = time() duration = t - socket.assigns.time @@ -155,7 +159,7 @@ defmodule NervesHubWeb.Live.Dashboard.Index do :ok end - defp time do + defp time() do System.monotonic_time(:millisecond) end end diff --git a/lib/nerves_hub_web/live/deployments/show.ex b/lib/nerves_hub_web/live/deployments/show.ex index c8561e4f5..b921537ea 100644 --- a/lib/nerves_hub_web/live/deployments/show.ex +++ b/lib/nerves_hub_web/live/deployments/show.ex @@ -6,7 +6,6 @@ defmodule NervesHubWeb.Live.Deployments.Show do alias NervesHub.Deployments.Deployment alias NervesHub.Devices alias NervesHub.Firmwares.Firmware - alias NervesHub.Repo alias NervesHubWeb.Components.AuditLogFeed @@ -140,7 +139,7 @@ defmodule NervesHubWeb.Live.Deployments.Show do end defp firmware_summary(%Deployment{firmware: %Ecto.Association.NotLoaded{}} = deployment) do - Repo.preload(deployment, [:firmware]) + Deployments.preload_firmware_and_archive(deployment) |> firmware_summary() end diff --git a/lib/nerves_hub_web/live/devices/index.ex b/lib/nerves_hub_web/live/devices/index.ex index 8cb62160a..e956dc838 100644 --- a/lib/nerves_hub_web/live/devices/index.ex +++ b/lib/nerves_hub_web/live/devices/index.ex @@ -12,10 +12,11 @@ defmodule NervesHubWeb.Live.Devices.Index do alias NervesHub.Firmwares alias NervesHub.Products.Product alias NervesHub.Tracker + alias NervesHub.Repo - alias Phoenix.Socket.Broadcast alias Phoenix.LiveView.JS + alias Phoenix.Socket.Broadcast alias NervesHubWeb.LayoutView.DateTimeFormat diff --git a/lib/nerves_hub_web/live/devices/settings.ex b/lib/nerves_hub_web/live/devices/settings.ex index 02617c8cf..37e6537f1 100644 --- a/lib/nerves_hub_web/live/devices/settings.ex +++ b/lib/nerves_hub_web/live/devices/settings.ex @@ -151,7 +151,7 @@ defmodule NervesHubWeb.Live.Devices.Settings do {:ok, socket} end - defp extensions do + defp extensions() do for extension <- Extensions.list(), into: %{}, do: {extension, Extensions.module(extension).description()} diff --git a/lib/nerves_hub_web/live/devices/show.ex b/lib/nerves_hub_web/live/devices/show.ex index 904200f7d..3c7778d11 100644 --- a/lib/nerves_hub_web/live/devices/show.ex +++ b/lib/nerves_hub_web/live/devices/show.ex @@ -19,8 +19,8 @@ defmodule NervesHubWeb.Live.Devices.Show do alias NervesHubWeb.Components.AuditLogFeed alias NervesHubWeb.Components.DeviceHeader - alias NervesHubWeb.Components.FwupProgress alias NervesHubWeb.Components.DeviceLocation + alias NervesHubWeb.Components.FwupProgress alias NervesHubWeb.Components.Utils alias Phoenix.Socket.Broadcast diff --git a/lib/nerves_hub_web/live/firmware.ex b/lib/nerves_hub_web/live/firmware.ex index ee7f8fcc8..bceeef1ba 100644 --- a/lib/nerves_hub_web/live/firmware.ex +++ b/lib/nerves_hub_web/live/firmware.ex @@ -1,7 +1,8 @@ defmodule NervesHubWeb.Live.Firmware do use NervesHubWeb, :updated_live_view - alias NervesHub.{Accounts, Firmwares} + alias NervesHub.Accounts + alias NervesHub.Firmwares embed_templates("firmware_templates/*") diff --git a/lib/nerves_hub_web/live/org/certificate_authorities.ex b/lib/nerves_hub_web/live/org/certificate_authorities.ex index 076b034b6..b872e2a43 100644 --- a/lib/nerves_hub_web/live/org/certificate_authorities.ex +++ b/lib/nerves_hub_web/live/org/certificate_authorities.ex @@ -1,12 +1,15 @@ defmodule NervesHubWeb.Live.Org.CertificateAuthorities do use NervesHubWeb, :updated_live_view - alias NervesHub.{Devices, Certificate} + alias NervesHub.Certificate + alias NervesHub.Devices alias NervesHub.Devices.CACertificate alias NervesHub.Devices.CACertificate.CSR alias NervesHub.Products + + alias NervesHubWeb.Components.CAHelpers + alias NervesHubWeb.Components.Utils alias NervesHubWeb.LayoutView.DateTimeFormat - alias NervesHubWeb.Components.{CAHelpers, Utils} embed_templates("certificate_authority_templates/*") diff --git a/lib/nerves_hub_web/live/org/products.ex b/lib/nerves_hub_web/live/org/products.ex index eebfcee96..22b149a76 100644 --- a/lib/nerves_hub_web/live/org/products.ex +++ b/lib/nerves_hub_web/live/org/products.ex @@ -56,7 +56,7 @@ defmodule NervesHubWeb.Live.Org.Products do end end - defp extensions do + defp extensions() do for extension <- Extensions.list(), into: %{}, do: {extension, Extensions.module(extension).description()} diff --git a/lib/nerves_hub_web/live/org/users.ex b/lib/nerves_hub_web/live/org/users.ex index 27cf8b2ec..90731aba3 100644 --- a/lib/nerves_hub_web/live/org/users.ex +++ b/lib/nerves_hub_web/live/org/users.ex @@ -2,7 +2,9 @@ defmodule NervesHubWeb.Live.Org.Users do use NervesHubWeb, :updated_live_view alias NervesHub.Accounts - alias NervesHub.Accounts.{Invite, Org, OrgUser} + alias NervesHub.Accounts.Invite + alias NervesHub.Accounts.Org + alias NervesHub.Accounts.OrgUser alias NervesHub.Accounts.SwooshEmail alias NervesHub.SwooshMailer alias NervesHubWeb.Components.Utils diff --git a/lib/nerves_hub_web/live/product/settings.ex b/lib/nerves_hub_web/live/product/settings.ex index ad33d88b9..b74c0bbdc 100644 --- a/lib/nerves_hub_web/live/product/settings.ex +++ b/lib/nerves_hub_web/live/product/settings.ex @@ -99,7 +99,7 @@ defmodule NervesHubWeb.Live.Product.Settings do {:noreply, socket} end - defp extensions do + defp extensions() do for extension <- Extensions.list(), into: %{}, do: {extension, Extensions.module(extension).description()} diff --git a/lib/nerves_hub_web/live/support_scripts/index.ex b/lib/nerves_hub_web/live/support_scripts/index.ex index a374a1d0d..b31d0ad39 100644 --- a/lib/nerves_hub_web/live/support_scripts/index.ex +++ b/lib/nerves_hub_web/live/support_scripts/index.ex @@ -2,6 +2,7 @@ defmodule NervesHubWeb.Live.SupportScripts.Index do use NervesHubWeb, :updated_live_view alias NervesHub.Scripts + alias NervesHub.Repo def mount(_params, _session, socket) do diff --git a/lib/nerves_hub_web/plugs/api/user.ex b/lib/nerves_hub_web/plugs/api/user.ex index 60afe70ff..425c673d4 100644 --- a/lib/nerves_hub_web/plugs/api/user.ex +++ b/lib/nerves_hub_web/plugs/api/user.ex @@ -15,7 +15,7 @@ defmodule NervesHubWeb.API.Plugs.User do assign(conn, :user, user) _error -> - raise NervesHubWeb.Unauthorized + raise NervesHubWeb.UnauthorizedError end end diff --git a/mix.exs b/mix.exs index fb84a033e..cc4737fbd 100644 --- a/mix.exs +++ b/mix.exs @@ -77,6 +77,7 @@ defmodule NervesHub.MixProject do {:circular_buffer, "~> 0.4.1"}, {:comeonin, "~> 5.3"}, {:contex, "~> 0.5.0"}, + {:credo, "~> 1.7", only: [:dev, :test], runtime: false}, {:crontab, "~> 1.1"}, {:decorator, "~> 1.2"}, {:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false}, diff --git a/mix.lock b/mix.lock index 5108a42de..a0b7771d8 100644 --- a/mix.lock +++ b/mix.lock @@ -4,6 +4,7 @@ "bandit": {:hex, :bandit, "1.6.3", "36591efd4bcf0e0508c16aee42b574b6c374077f7b96575ff46c519c827db144", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "158a9802ec02ac297689948da8ce529a915528be11cb8fe0f27d1346864f50c0"}, "base62": {:hex, :base62, "1.2.2", "85c6627eb609317b70f555294045895ffaaeb1758666ab9ef9ca38865b11e629", [:mix], [{:custom_base, "~> 0.2.1", [hex: :custom_base, repo: "hexpm", optional: false]}], "hexpm", "d41336bda8eaa5be197f1e4592400513ee60518e5b9f4dcf38f4b4dae6f377bb"}, "bcrypt_elixir": {:hex, :bcrypt_elixir, "3.2.0", "feab711974beba4cb348147170346fe097eea2e840db4e012a145e180ed4ab75", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "563e92a6c77d667b19c5f4ba17ab6d440a085696bdf4c68b9b0f5b30bc5422b8"}, + "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "castore": {:hex, :castore, "1.0.11", "4bbd584741601eb658007339ea730b082cc61f3554cf2e8f39bf693a11b49073", [:mix], [], "hexpm", "e03990b4db988df56262852f20de0f659871c35154691427a5047f4967a16a62"}, "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, "chatterbox": {:hex, :ts_chatterbox, "0.15.1", "5cac4d15dd7ad61fc3c4415ce4826fc563d4643dee897a558ec4ea0b1c835c9c", [:rebar3], [{:hpack, "~> 0.3.0", [hex: :hpack_erl, repo: "hexpm", optional: false]}], "hexpm", "4f75b91451338bc0da5f52f3480fa6ef6e3a2aeecfc33686d6b3d0a0948f31aa"}, @@ -11,6 +12,7 @@ "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, "comeonin": {:hex, :comeonin, "5.5.0", "364d00df52545c44a139bad919d7eacb55abf39e86565878e17cebb787977368", [:mix], [], "hexpm", "6287fc3ba0aad34883cbe3f7949fc1d1e738e5ccdce77165bc99490aa69f47fb"}, "contex": {:hex, :contex, "0.5.0", "5d8a6defbeb41f54adfcb0f85c4756d4f2b84aa5b0d809d45a5d2e90d91d0392", [:mix], [{:nimble_strftime, "~> 0.1.0", [hex: :nimble_strftime, repo: "hexpm", optional: false]}], "hexpm", "b7497a1790324d84247859df44ba4bcf2489d9bba1812a5375b2f2046b9e6fd7"}, + "credo": {:hex, :credo, "1.7.11", "d3e805f7ddf6c9c854fd36f089649d7cf6ba74c42bc3795d587814e3c9847102", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "56826b4306843253a66e47ae45e98e7d284ee1f95d53d1612bb483f88a8cf219"}, "crontab": {:hex, :crontab, "1.1.14", "233fcfdc2c74510cabdbcb800626babef414e7cb13cea11ddf62e10e16e2bf76", [:mix], [{:ecto, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "4e3b9950bc22ae8d0395ffb5f4b127a140005cba95745abf5ff9ee7e8203c6fa"}, "ctx": {:hex, :ctx, "0.6.0", "8ff88b70e6400c4df90142e7f130625b82086077a45364a78d208ed3ed53c7fe", [:rebar3], [], "hexpm", "a14ed2d1b67723dbebbe423b28d7615eb0bdcba6ff28f2d1f1b0a7e1d4aa5fc2"}, "custom_base": {:hex, :custom_base, "0.2.1", "4a832a42ea0552299d81652aa0b1f775d462175293e99dfbe4d7dbaab785a706", [:mix], [], "hexpm", "8df019facc5ec9603e94f7270f1ac73ddf339f56ade76a721eaa57c1493ba463"}, diff --git a/test/nerves_hub/accounts/accounts_test.exs b/test/nerves_hub/accounts/accounts_test.exs index 4ebd1d195..5d56e2e86 100644 --- a/test/nerves_hub/accounts/accounts_test.exs +++ b/test/nerves_hub/accounts/accounts_test.exs @@ -4,7 +4,11 @@ defmodule NervesHub.AccountsTest do alias Ecto.Changeset alias NervesHub.Accounts - alias NervesHub.Accounts.{Org, OrgKey, OrgUser, User, Invite} + alias NervesHub.Accounts.Invite + alias NervesHub.Accounts.Org + alias NervesHub.Accounts.OrgKey + alias NervesHub.Accounts.OrgUser + alias NervesHub.Accounts.User alias NervesHub.Fixtures @required_org_params %{name: "Org"} diff --git a/test/nerves_hub/accounts/email_test.exs b/test/nerves_hub/accounts/email_test.exs index dca42dc30..22b1e9c6f 100644 --- a/test/nerves_hub/accounts/email_test.exs +++ b/test/nerves_hub/accounts/email_test.exs @@ -3,7 +3,8 @@ defmodule NervesHub.Accounts.EmailTest do import Swoosh.TestAssertions - alias NervesHub.Accounts.{SwooshEmail, User} + alias NervesHub.Accounts.SwooshEmail + alias NervesHub.Accounts.User alias NervesHub.EmailView alias NervesHub.SwooshMailer diff --git a/test/nerves_hub/accounts/remove_account_test.exs b/test/nerves_hub/accounts/remove_account_test.exs index 690ac4100..1113e1ea9 100644 --- a/test/nerves_hub/accounts/remove_account_test.exs +++ b/test/nerves_hub/accounts/remove_account_test.exs @@ -1,7 +1,9 @@ defmodule NervesHub.Accounts.RemoveAccountTest do use NervesHub.DataCase, async: true - alias NervesHub.{Accounts, Fixtures} + alias NervesHub.Accounts + alias NervesHub.Accounts.RemoveAccount + alias NervesHub.Fixtures test "remove_account for basic account" do user = Fixtures.user_fixture() @@ -11,7 +13,7 @@ defmodule NervesHub.Accounts.RemoveAccountTest do firmware = Fixtures.firmware_fixture(org_key, product) Fixtures.deployment_fixture(org, firmware) - NervesHub.Accounts.RemoveAccount.remove_account(user.id) + RemoveAccount.remove_account(user.id) assert {:error, :not_found} = Accounts.get_user(user.id) end @@ -51,7 +53,7 @@ defmodule NervesHub.Accounts.RemoveAccountTest do org3_firmware = Fixtures.firmware_fixture(org3_key, org3_product) Fixtures.deployment_fixture(org, org3_firmware) - NervesHub.Accounts.RemoveAccount.remove_account(user.id) + RemoveAccount.remove_account(user.id) assert {:error, _} = Accounts.get_user(user.id) assert {:error, _} = Accounts.get_org(org.id) diff --git a/test/nerves_hub/certificates_test.exs b/test/nerves_hub/certificates_test.exs index bac55ac85..23060e027 100644 --- a/test/nerves_hub/certificates_test.exs +++ b/test/nerves_hub/certificates_test.exs @@ -1,7 +1,8 @@ defmodule NervesHub.CertificatesTest do use NervesHub.DataCase, async: true - alias NervesHub.{Certificate, Fixtures} + alias NervesHub.Certificate + alias NervesHub.Fixtures setup_all do cert = diff --git a/test/nerves_hub/deployments_test.exs b/test/nerves_hub/deployments_test.exs index 5460413d6..2dfc3959b 100644 --- a/test/nerves_hub/deployments_test.exs +++ b/test/nerves_hub/deployments_test.exs @@ -1,10 +1,12 @@ defmodule NervesHub.DeploymentsTest do use NervesHub.DataCase, async: false + import Phoenix.ChannelTest alias NervesHub.Deployments alias NervesHub.Devices.Device alias NervesHub.Fixtures + alias Ecto.Changeset setup do diff --git a/test/nerves_hub/devices/ca_certificate/csr_test.exs b/test/nerves_hub/devices/ca_certificate/csr_test.exs index 4fc3c3c07..b98f4eae4 100644 --- a/test/nerves_hub/devices/ca_certificate/csr_test.exs +++ b/test/nerves_hub/devices/ca_certificate/csr_test.exs @@ -1,7 +1,8 @@ defmodule NervesHub.Devices.CACertificate.CSRTest do use ExUnit.Case - alias NervesHub.Devices.CACertificate.CSR + alias NervesHub.Certificate + alias NervesHub.Devices.CACertificate.CSR @tag :tmp_dir test "valid csr", %{tmp_dir: dir} do @@ -67,7 +68,7 @@ defmodule NervesHub.Devices.CACertificate.CSRTest do end defp openssl(args, dir) do - {_, 0} = System.cmd("openssl", args, cd: dir, stderr_to_stdout: true) + {_, 0} = System.cmd("openssl", args, cd: dir, stderr_to_stdout: true, env: []) :ok end end diff --git a/test/nerves_hub/devices_test.exs b/test/nerves_hub/devices_test.exs index 2c905c2bd..b30da5ba9 100644 --- a/test/nerves_hub/devices_test.exs +++ b/test/nerves_hub/devices_test.exs @@ -1,6 +1,8 @@ defmodule NervesHub.DevicesTest do use NervesHub.DataCase, async: false + alias Ecto.Changeset + alias NervesHub.AuditLogs alias NervesHub.Deployments alias NervesHub.Devices @@ -9,8 +11,8 @@ defmodule NervesHub.DevicesTest do alias NervesHub.Firmwares alias NervesHub.Fixtures alias NervesHub.Products + alias NervesHub.Repo - alias Ecto.Changeset @valid_fwup_version "1.10.0" diff --git a/test/nerves_hub/firmwares_test.exs b/test/nerves_hub/firmwares_test.exs index c6f9a18d0..58bb8f958 100644 --- a/test/nerves_hub/firmwares_test.exs +++ b/test/nerves_hub/firmwares_test.exs @@ -1,18 +1,16 @@ defmodule NervesHub.FirmwaresTest do use NervesHub.DataCase, async: true - alias NervesHub.{ - Firmwares, - Firmwares.Firmware, - Repo, - Fixtures, - Support.Fwup, - DeltaUpdaterMock, - UploadMock - } - alias Ecto.Changeset + alias NervesHub.DeltaUpdaterMock + alias NervesHub.Firmwares + alias NervesHub.Firmwares.Firmware + alias NervesHub.Fixtures + alias NervesHub.Repo + alias NervesHub.Support.Fwup + alias NervesHub.UploadMock + setup context do Mox.verify_on_exit!(context) user = Fixtures.user_fixture() diff --git a/test/nerves_hub/fwup_test.exs b/test/nerves_hub/fwup_test.exs index cd0cb570d..f517c19c3 100644 --- a/test/nerves_hub/fwup_test.exs +++ b/test/nerves_hub/fwup_test.exs @@ -1,9 +1,9 @@ defmodule NervesHub.FwupTest do use NervesHub.DataCase, async: true + alias NervesHub.Fixtures alias NervesHub.Fwup alias NervesHub.Support.Fwup, as: SupportFwup - alias NervesHub.Fixtures test "retrieves all fwup metadata", %{tmp_dir: tmp_dir} do user = Fixtures.user_fixture() diff --git a/test/nerves_hub/products/products_test.exs b/test/nerves_hub/products/products_test.exs index 9009ca0c9..700aefeef 100644 --- a/test/nerves_hub/products/products_test.exs +++ b/test/nerves_hub/products/products_test.exs @@ -1,8 +1,9 @@ defmodule NervesHub.ProductsTest do use NervesHub.DataCase, async: true + alias NervesHub.Accounts alias NervesHub.Fixtures - alias NervesHub.{Products, Accounts} + alias NervesHub.Products describe "products" do alias NervesHub.Products.Product diff --git a/test/nerves_hub/scripts_test.exs b/test/nerves_hub/scripts_test.exs index 36505021c..89a52293b 100644 --- a/test/nerves_hub/scripts_test.exs +++ b/test/nerves_hub/scripts_test.exs @@ -1,8 +1,8 @@ defmodule NervesHub.ScriptsTest do use NervesHub.DataCase - alias NervesHub.Scripts alias NervesHub.Fixtures + alias NervesHub.Scripts setup do user = Fixtures.user_fixture() diff --git a/test/nerves_hub/workers/device_health_truncation_test.exs b/test/nerves_hub/workers/device_health_truncation_test.exs index 16a9462f3..799a5ade0 100644 --- a/test/nerves_hub/workers/device_health_truncation_test.exs +++ b/test/nerves_hub/workers/device_health_truncation_test.exs @@ -1,8 +1,8 @@ defmodule NervesHub.Workers.DeviceHealthTruncationTest do use NervesHub.DataCase - alias NervesHub.Fixtures alias NervesHub.Devices + alias NervesHub.Fixtures alias NervesHub.Workers.DeviceHealthTruncation test "delete device health and metrics entries older than 7 days", %{tmp_dir: dir} do diff --git a/test/nerves_hub/workers/schedule_org_audit_log_truncation_test.exs b/test/nerves_hub/workers/schedule_org_audit_log_truncation_test.exs index c4d718d9c..d7f91b5f6 100644 --- a/test/nerves_hub/workers/schedule_org_audit_log_truncation_test.exs +++ b/test/nerves_hub/workers/schedule_org_audit_log_truncation_test.exs @@ -3,8 +3,8 @@ defmodule NervesHub.Workers.ScheduleOrgAuditLogTruncationTest do alias NervesHub.Accounts alias NervesHub.Fixtures - alias NervesHub.Workers.ScheduleOrgAuditLogTruncation alias NervesHub.Workers.OrgAuditLogTruncation + alias NervesHub.Workers.ScheduleOrgAuditLogTruncation setup do Application.put_env(:nerves_hub, :audit_logs, enabled: true) diff --git a/test/nerves_hub_web/channels/extensions_channel_test.exs b/test/nerves_hub_web/channels/extensions_channel_test.exs index 461f67567..f2e68759e 100644 --- a/test/nerves_hub_web/channels/extensions_channel_test.exs +++ b/test/nerves_hub_web/channels/extensions_channel_test.exs @@ -2,8 +2,8 @@ defmodule NervesHubWeb.ExtensionsChannelTest do use NervesHubWeb.ChannelCase use DefaultMocks - alias NervesHub.Products alias NervesHub.Fixtures + alias NervesHub.Products alias NervesHubWeb.DeviceChannel alias NervesHubWeb.DeviceSocket alias NervesHubWeb.ExtensionsChannel diff --git a/test/nerves_hub_web/channels/websocket_test.exs b/test/nerves_hub_web/channels/websocket_test.exs index 46834e347..07e8582fc 100644 --- a/test/nerves_hub_web/channels/websocket_test.exs +++ b/test/nerves_hub_web/channels/websocket_test.exs @@ -7,13 +7,13 @@ defmodule NervesHubWeb.WebsocketTest do alias NervesHub.AuditLogs alias NervesHub.AuditLogs.AuditLog - alias NervesHub.Fixtures alias NervesHub.Deployments alias NervesHub.Deployments.Orchestrator alias NervesHub.Devices alias NervesHub.Devices.Connections alias NervesHub.Devices.Device alias NervesHub.Devices.DeviceConnection + alias NervesHub.Fixtures alias NervesHub.Products alias NervesHub.Repo alias NervesHubWeb.DeviceEndpoint diff --git a/test/nerves_hub_web/controllers/api/ca_certificate_controller_test.exs b/test/nerves_hub_web/controllers/api/ca_certificate_controller_test.exs index 53c6b5f1c..5b5691fd9 100644 --- a/test/nerves_hub_web/controllers/api/ca_certificate_controller_test.exs +++ b/test/nerves_hub_web/controllers/api/ca_certificate_controller_test.exs @@ -1,7 +1,8 @@ defmodule NervesHubWeb.API.CACertificateControllerTest do use NervesHubWeb.APIConnCase, async: true - alias NervesHub.{Devices, Certificate} + alias NervesHub.Certificate + alias NervesHub.Devices describe "index" do test "lists all ca certificates", %{conn: conn, org: org} do diff --git a/test/nerves_hub_web/controllers/api/deployment_controller_test.exs b/test/nerves_hub_web/controllers/api/deployment_controller_test.exs index 3f6ac7d95..45a7012cc 100644 --- a/test/nerves_hub_web/controllers/api/deployment_controller_test.exs +++ b/test/nerves_hub_web/controllers/api/deployment_controller_test.exs @@ -1,7 +1,9 @@ defmodule NervesHubWeb.API.DeploymentControllerTest do use NervesHubWeb.APIConnCase, async: true - alias NervesHub.{AuditLogs, Deployments.Deployment, Fixtures} + alias NervesHub.AuditLogs + alias NervesHub.Deployments.Deployment + alias NervesHub.Fixtures describe "index" do test "lists all deployments", %{conn: conn, org: org, product: product} do diff --git a/test/nerves_hub_web/controllers/api/device_certificate_controller_test.exs b/test/nerves_hub_web/controllers/api/device_certificate_controller_test.exs index e14f21bc4..8f657a7dc 100644 --- a/test/nerves_hub_web/controllers/api/device_certificate_controller_test.exs +++ b/test/nerves_hub_web/controllers/api/device_certificate_controller_test.exs @@ -1,7 +1,9 @@ defmodule NervesHubWeb.API.DeviceCertificateControllerTest do use NervesHubWeb.APIConnCase, async: true - alias NervesHub.{Certificate, Devices, Fixtures} + alias NervesHub.Certificate + alias NervesHub.Devices + alias NervesHub.Fixtures setup %{org: org, product: product} do identifier = "device-1234" diff --git a/test/nerves_hub_web/controllers/api/key_controller_test.exs b/test/nerves_hub_web/controllers/api/key_controller_test.exs index 757ca5465..d27b0ec4b 100644 --- a/test/nerves_hub_web/controllers/api/key_controller_test.exs +++ b/test/nerves_hub_web/controllers/api/key_controller_test.exs @@ -1,9 +1,9 @@ defmodule NervesHubWeb.API.KeyControllerTest do use NervesHubWeb.APIConnCase, async: true + alias NervesHub.Accounts alias NervesHub.Fixtures alias NervesHub.Support.Fwup - alias NervesHub.Accounts describe "index" do test "lists all keys", %{conn: conn, org: org} do diff --git a/test/nerves_hub_web/controllers/api/org_user_controller_test.exs b/test/nerves_hub_web/controllers/api/org_user_controller_test.exs index da16d1f25..5ba28bf36 100644 --- a/test/nerves_hub_web/controllers/api/org_user_controller_test.exs +++ b/test/nerves_hub_web/controllers/api/org_user_controller_test.exs @@ -3,8 +3,8 @@ defmodule NervesHubWeb.API.OrgUserControllerTest do import Swoosh.TestAssertions - alias NervesHub.Fixtures alias NervesHub.Accounts + alias NervesHub.Fixtures setup context do org = Fixtures.org_fixture(context.user, %{name: "api_test"}) diff --git a/test/nerves_hub_web/controllers/password_reset_controller_test.exs b/test/nerves_hub_web/controllers/password_reset_controller_test.exs index 11cf4b413..06e144927 100644 --- a/test/nerves_hub_web/controllers/password_reset_controller_test.exs +++ b/test/nerves_hub_web/controllers/password_reset_controller_test.exs @@ -3,8 +3,9 @@ defmodule NervesHubWeb.PasswordResetControllerTest do import Swoosh.TestAssertions - alias NervesHub.Fixtures alias NervesHub.Accounts + alias NervesHub.Accounts.SwooshEmail + alias NervesHub.Fixtures describe "new password_reset" do test "renders form", %{conn: conn} do @@ -24,7 +25,7 @@ defmodule NervesHubWeb.PasswordResetControllerTest do assert redirected_to(reset_conn) == Routes.session_path(reset_conn, :new) {:ok, updated_user} = Accounts.get_user(user.id) - assert_email_sent(NervesHub.Accounts.SwooshEmail.forgot_password(updated_user)) + assert_email_sent(SwooshEmail.forgot_password(updated_user)) end test "with invalid params", %{conn: conn} do diff --git a/test/nerves_hub_web/live/devices/index_test.exs b/test/nerves_hub_web/live/devices/index_test.exs index ca460cf1d..5e7f8a682 100644 --- a/test/nerves_hub_web/live/devices/index_test.exs +++ b/test/nerves_hub_web/live/devices/index_test.exs @@ -4,9 +4,11 @@ defmodule NervesHubWeb.Live.Devices.IndexTest do alias NervesHub.Devices alias NervesHub.Firmwares.FirmwareMetadata alias NervesHub.Fixtures - alias NervesHubWeb.Endpoint + alias NervesHub.Repo + alias NervesHubWeb.Endpoint + setup %{fixture: %{device: device}} do Endpoint.subscribe("device:#{device.id}") end diff --git a/test/nerves_hub_web/live/devices/show_test.exs b/test/nerves_hub_web/live/devices/show_test.exs index a4405268e..37d3795b2 100644 --- a/test/nerves_hub_web/live/devices/show_test.exs +++ b/test/nerves_hub_web/live/devices/show_test.exs @@ -53,7 +53,7 @@ defmodule NervesHubWeb.Live.Devices.ShowTest do Process.flag(:trap_exit, true) - assert {{%NervesHub.Errors.Unauthorized{}, _}, _} = + assert {{%NervesHubWeb.UnauthorizedError{}, _}, _} = catch_exit(render_change(view, :reboot, %{})) end end @@ -494,7 +494,7 @@ defmodule NervesHubWeb.Live.Devices.ShowTest do device: device, deployment: deployment } do - assert length(AuditLogs.logs_for(device)) == 0 + assert Enum.empty?(AuditLogs.logs_for(device)) conn |> visit("/org/#{org.name}/#{product.name}/devices/#{device.identifier}") diff --git a/test/nerves_hub_web/live/org/certificate_authorities_test.exs b/test/nerves_hub_web/live/org/certificate_authorities_test.exs index 6e4d7f333..7542a0e96 100644 --- a/test/nerves_hub_web/live/org/certificate_authorities_test.exs +++ b/test/nerves_hub_web/live/org/certificate_authorities_test.exs @@ -1,8 +1,9 @@ defmodule NervesHubWeb.Live.Org.CertificateAuthoritiesTest do use NervesHubWeb.ConnCase.Browser, async: true + alias NervesHub.Certificate + alias NervesHub.Devices alias NervesHub.Fixtures - alias NervesHub.{Certificate, Devices} alias NervesHubWeb.Components.Utils describe "index" do diff --git a/test/nerves_hub_web/live/support_scripts_test.exs b/test/nerves_hub_web/live/support_scripts_test.exs index c0a116393..879e897bf 100644 --- a/test/nerves_hub_web/live/support_scripts_test.exs +++ b/test/nerves_hub_web/live/support_scripts_test.exs @@ -1,8 +1,8 @@ defmodule NervesHubWeb.Live.SupportScriptsTest do use NervesHubWeb.ConnCase.Browser, async: true - alias NervesHub.Scripts alias NervesHub.Fixtures + alias NervesHub.Scripts setup %{user: user, org: org} do [product: Fixtures.product_fixture(user, org, %{name: "Amazing"})] diff --git a/test/nerves_hub_web/plugs/api/user_test.exs b/test/nerves_hub_web/plugs/api/user_test.exs index 5f4052aed..355ead961 100644 --- a/test/nerves_hub_web/plugs/api/user_test.exs +++ b/test/nerves_hub_web/plugs/api/user_test.exs @@ -27,14 +27,14 @@ defmodule NervesHubWeb.API.Plugs.UserTest do end test "rejects unknown API token" do - assert_raise(NervesHubWeb.Unauthorized, fn -> + assert_raise(NervesHubWeb.UnauthorizedError, fn -> build_conn() |> put_req_header("authorization", "token wat-is-this-token") |> put_req_header("accept", "application/json") |> get("/api/users/me") end) - assert_raise(NervesHubWeb.Unauthorized, fn -> + assert_raise(NervesHubWeb.UnauthorizedError, fn -> build_conn() |> put_req_header("authorization", "token nhu_1234567890abcdefghijklmnopqrstuvwxyz") |> put_req_header("accept", "application/json") diff --git a/test/nerves_hub_web/plugs/org_user_test.exs b/test/nerves_hub_web/plugs/org_user_test.exs index 5c465e23a..47802d5d1 100644 --- a/test/nerves_hub_web/plugs/org_user_test.exs +++ b/test/nerves_hub_web/plugs/org_user_test.exs @@ -2,7 +2,8 @@ defmodule NervesHubWeb.Plugs.OrgUserTest do use NervesHubWeb.ConnCase.Browser, async: true use Plug.Test - alias NervesHub.{Accounts, Fixtures} + alias NervesHub.Accounts + alias NervesHub.Fixtures setup context do {:ok, org_user} = Accounts.get_org_user(context.org, context.user) diff --git a/test/support/archives.ex b/test/support/archives.ex index b61bc1c0f..90466638b 100644 --- a/test/support/archives.ex +++ b/test/support/archives.ex @@ -1,5 +1,9 @@ defmodule NervesHub.Support.Archives do + @moduledoc false + defmodule MetaParams do + @moduledoc false + defstruct product: "nerves-hub", description: "Manifest", version: "1.0.0", @@ -22,13 +26,17 @@ defmodule NervesHub.Support.Archives do out_path = Path.join([dir, archive_name <> ".fw"]) {_, 0} = - System.cmd("fwup", [ - "-c", - "-f", - conf_path, - "-o", - out_path - ]) + System.cmd( + "fwup", + [ + "-c", + "-f", + conf_path, + "-o", + out_path + ], + env: [] + ) {:ok, out_path} end @@ -52,7 +60,8 @@ defmodule NervesHub.Support.Archives do "-o", output_path ], - stderr_to_stdout: true + stderr_to_stdout: true, + env: [] ) {:ok, output_path} diff --git a/test/support/fixtures.ex b/test/support/fixtures.ex index 3cbc1d30c..ccd7ab4cc 100644 --- a/test/support/fixtures.ex +++ b/test/support/fixtures.ex @@ -1,13 +1,15 @@ defmodule NervesHub.Fixtures do + @moduledoc false + alias NervesHub.Accounts alias NervesHub.Accounts.Org alias NervesHub.Accounts.OrgKey alias NervesHub.Archives alias NervesHub.AuditLogs alias NervesHub.Certificate + alias NervesHub.Deployments alias NervesHub.Devices alias NervesHub.Devices.InflightUpdate - alias NervesHub.Deployments alias NervesHub.Firmwares alias NervesHub.Products alias NervesHub.Products.Product @@ -288,7 +290,7 @@ defmodule NervesHub.Fixtures do end defp openssl(args, dir) do - {_, 0} = System.cmd("openssl", args, cd: dir, stderr_to_stdout: true) + {_, 0} = System.cmd("openssl", args, cd: dir, stderr_to_stdout: true, env: []) :ok end @@ -436,11 +438,11 @@ defmodule NervesHub.Fixtures do } end - defp counter do + defp counter() do System.unique_integer([:positive]) end - defp counter_in_alpha do + defp counter_in_alpha() do counter() |> Integer.to_string() |> String.split("") diff --git a/test/support/fwup.ex b/test/support/fwup.ex index 0166f8360..1099e651a 100644 --- a/test/support/fwup.ex +++ b/test/support/fwup.ex @@ -13,6 +13,8 @@ defmodule NervesHub.Support.Fwup do """ defmodule MetaParams do + @moduledoc false + defstruct product: "nerves-hub", description: "D", version: "1.0.0", @@ -22,6 +24,8 @@ defmodule NervesHub.Support.Fwup do end defmodule InvalidMetaParams do + @moduledoc false + defstruct description: "D", version: "1.0.0", platform: "platform", @@ -38,7 +42,7 @@ defmodule NervesHub.Support.Fwup do def gen_key_pair(key_name, dir \\ System.tmp_dir()) do key_path_no_extension = Path.join([dir, key_name]) - _ = System.cmd("fwup", ["-g", "-o", key_path_no_extension], stderr_to_stdout: true) + _ = System.cmd("fwup", ["-g", "-o", key_path_no_extension], stderr_to_stdout: true, env: []) :ok end @@ -58,13 +62,17 @@ defmodule NervesHub.Support.Fwup do out_path = Path.join([dir, firmware_name <> ".fw"]) {_, 0} = - System.cmd("fwup", [ - "-c", - "-f", - conf_path, - "-o", - out_path - ]) + System.cmd( + "fwup", + [ + "-c", + "-f", + conf_path, + "-o", + out_path + ], + env: [] + ) {:ok, out_path} end @@ -77,13 +85,17 @@ defmodule NervesHub.Support.Fwup do out_path = Path.join([dir, firmware_name <> ".fw"]) {_, 0} = - System.cmd("fwup", [ - "-c", - "-f", - conf_path, - "-o", - out_path - ]) + System.cmd( + "fwup", + [ + "-c", + "-f", + conf_path, + "-o", + out_path + ], + env: [] + ) {:ok, out_path} end @@ -107,7 +119,8 @@ defmodule NervesHub.Support.Fwup do "-o", output_path ], - stderr_to_stdout: true + stderr_to_stdout: true, + env: [] ) {:ok, output_path} @@ -130,7 +143,8 @@ defmodule NervesHub.Support.Fwup do {_, 0} = System.cmd("dd", ["if=" <> input_path, "of=" <> output_path, "bs=256", "count=1"], - stderr_to_stdout: true + stderr_to_stdout: true, + env: [] ) {:ok, output_path} diff --git a/test/support/socket_client.ex b/test/support/socket_client.ex index fe6f36b17..1e92700d1 100644 --- a/test/support/socket_client.ex +++ b/test/support/socket_client.ex @@ -1,4 +1,6 @@ defmodule SocketClient do + @moduledoc false + use Slipstream, restart: :temporary def start_link(args) do @@ -103,7 +105,7 @@ defmodule SocketClient do end end - @impl true + @impl Slipstream def init(config) do Process.flag(:trap_exit, true) @@ -126,7 +128,7 @@ defmodule SocketClient do {:ok, socket} end - @impl true + @impl Slipstream def handle_connect(socket) do socket = socket @@ -136,7 +138,7 @@ defmodule SocketClient do {:ok, socket} end - @impl true + @impl Slipstream def handle_join("device", reply, socket) do socket = socket @@ -155,7 +157,7 @@ defmodule SocketClient do {:ok, socket} end - @impl true + @impl Slipstream def handle_message("device", "extensions:get", _message, socket) do {:ok, socket} end @@ -186,7 +188,7 @@ defmodule SocketClient do {:ok, socket} end - @impl true + @impl Slipstream def handle_call(:connected?, _from, socket) do {:reply, socket.assigns.connected?, socket} end diff --git a/test/support/tracker_helper.ex b/test/support/tracker_helper.ex index ad11637f4..e91b4684c 100644 --- a/test/support/tracker_helper.ex +++ b/test/support/tracker_helper.ex @@ -1,4 +1,6 @@ defmodule TrackerHelper do + @moduledoc false + defmacro subscribe_for_updates(device) do quote do Phoenix.PubSub.subscribe(NervesHub.PubSub, "device:#{unquote(device).identifier}:internal")