mirror of
https://github.com/mirego/accent.git
synced 2024-08-16 14:30:38 +03:00
Refactor hook to unify in movement persister module
This commit is contained in:
parent
d718fc0020
commit
88789980fc
@ -13,17 +13,14 @@ if config_env() == :dev do
|
||||
end
|
||||
|
||||
if config_env() == :test do
|
||||
events = ~w(sync add_translations create_collaborator create_comment complete_review new_conflicts)
|
||||
|
||||
config :accent, Accent.Hook, outbounds: [{Accent.Hook.Outbounds.Mock, events: events}]
|
||||
config :accent, Accent.Hook, outbounds: [Accent.Hook.Outbounds.Mock]
|
||||
else
|
||||
config :accent, Accent.Hook,
|
||||
outbounds: [
|
||||
{Accent.Hook.Outbounds.Discord, events: ~w(sync complete_review new_conflicts)},
|
||||
{Accent.Hook.Outbounds.Email, events: ~w(create_collaborator create_comment)},
|
||||
{Accent.Hook.Outbounds.Slack, events: ~w(sync complete_review new_conflicts)},
|
||||
{Accent.Hook.Outbounds.Websocket,
|
||||
events: ~w(sync create_collaborator create_comment complete_review new_conflicts)}
|
||||
Accent.Hook.Outbounds.Discord,
|
||||
Accent.Hook.Outbounds.Email,
|
||||
Accent.Hook.Outbounds.Slack,
|
||||
Accent.Hook.Outbounds.Websocket
|
||||
]
|
||||
end
|
||||
|
||||
|
@ -2,6 +2,7 @@ defmodule Accent.IntegrationManager.Execute.AzureStorageContainer do
|
||||
@moduledoc false
|
||||
|
||||
alias Accent.Document
|
||||
alias Accent.Hook
|
||||
alias Accent.Repo
|
||||
alias Accent.Revision
|
||||
alias Accent.Scopes.Document, as: DocumentScope
|
||||
@ -11,12 +12,14 @@ defmodule Accent.IntegrationManager.Execute.AzureStorageContainer do
|
||||
alias Accent.Translation
|
||||
alias Accent.Version
|
||||
|
||||
def upload_translations(integration, params) do
|
||||
def upload_translations(integration, user, params) do
|
||||
project = Repo.one!(Ecto.assoc(integration, :project))
|
||||
version = fetch_version(project, params)
|
||||
documents = fetch_documents(project)
|
||||
revisions = fetch_revisions(project)
|
||||
master_revision = Repo.preload(Repo.one!(RevisionScope.master(Ecto.assoc(project, :revisions))), :language)
|
||||
|
||||
master_revision =
|
||||
Repo.preload(Repo.one!(RevisionScope.master(Ecto.assoc(project, :revisions))), :language)
|
||||
|
||||
uploads =
|
||||
Enum.flat_map(documents, fn document ->
|
||||
@ -32,30 +35,54 @@ defmodule Accent.IntegrationManager.Execute.AzureStorageContainer do
|
||||
}
|
||||
|
||||
%{render: render} = Accent.TranslationsRenderer.render_translations(render_options)
|
||||
[%{document: %{document | render: render}, language: Accent.Revision.language(revision)}]
|
||||
|
||||
[
|
||||
%{
|
||||
document: %{document | render: render},
|
||||
language: Accent.Revision.language(revision)
|
||||
}
|
||||
]
|
||||
else
|
||||
[]
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
for upload <- uploads do
|
||||
file = Path.join([System.tmp_dir(), Accent.Utils.SecureRandom.urlsafe_base64(16)])
|
||||
:ok = File.write(file, upload.document.render)
|
||||
uri = URI.parse(integration.data.azure_storage_container_sas)
|
||||
|
||||
uri = URI.parse(integration.data.azure_storage_container_sas)
|
||||
extension = Accent.DocumentFormat.extension_by_format(upload.document.format)
|
||||
version_tag = (version && version.tag) || "latest"
|
||||
|
||||
path =
|
||||
Path.join([
|
||||
uri.path,
|
||||
(version && version.tag) || "latest",
|
||||
upload.language.slug,
|
||||
upload.document.path <> "." <> extension
|
||||
])
|
||||
document_urls =
|
||||
for upload <- uploads do
|
||||
file = Path.join([System.tmp_dir(), Accent.Utils.SecureRandom.urlsafe_base64(16)])
|
||||
:ok = File.write(file, upload.document.render)
|
||||
|
||||
HTTPoison.put(URI.to_string(%{uri | path: path}), {:file, file}, [{"x-ms-blob-type", "BlockBlob"}])
|
||||
end
|
||||
extension = Accent.DocumentFormat.extension_by_format(upload.document.format)
|
||||
document_name = upload.document.path <> "." <> extension
|
||||
|
||||
path =
|
||||
Path.join([
|
||||
uri.path,
|
||||
version_tag,
|
||||
upload.language.slug,
|
||||
document_name
|
||||
])
|
||||
|
||||
url = URI.to_string(%{uri | path: path})
|
||||
# HTTPoison.put(url, {:file, file}, [{"x-ms-blob-type", "BlockBlob"}])
|
||||
|
||||
%{name: document_name, url: url}
|
||||
end
|
||||
|
||||
Hook.outbound(%Hook.Context{
|
||||
event: "integration_execute_azure_storage_container",
|
||||
project_id: project.id,
|
||||
user_id: user.id,
|
||||
payload: %{
|
||||
version_tag: version_tag,
|
||||
document_urls: document_urls
|
||||
}
|
||||
})
|
||||
|
||||
:ok
|
||||
end
|
||||
|
@ -23,7 +23,7 @@ defmodule Accent.IntegrationManager do
|
||||
|
||||
@spec execute(Integration.t(), User.t(), map()) :: {:ok, Integration.t()}
|
||||
def execute(integration, user, params) do
|
||||
case execute_integration(integration, params) do
|
||||
case execute_integration(integration, user, params) do
|
||||
:ok ->
|
||||
integration
|
||||
|> change(%{last_executed_at: DateTime.utc_now(), last_executed_by_user_id: user.id})
|
||||
@ -51,16 +51,17 @@ defmodule Accent.IntegrationManager do
|
||||
|> validate_required([:service, :data])
|
||||
end
|
||||
|
||||
defp execute_integration(%{service: "azure_storage_container"} = integration, params) do
|
||||
defp execute_integration(%{service: "azure_storage_container"} = integration, user, params) do
|
||||
Accent.IntegrationManager.Execute.AzureStorageContainer.upload_translations(
|
||||
integration,
|
||||
user,
|
||||
params[:azure_storage_container]
|
||||
)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
defp execute_integration(_integration, _params) do
|
||||
defp execute_integration(_integration, _user, _params) do
|
||||
:noop
|
||||
end
|
||||
|
||||
|
@ -15,6 +15,7 @@ defmodule Accent.GraphQL.Types.Integration do
|
||||
value(:complete_review, as: "complete_review")
|
||||
value(:create_collaborator, as: "create_collaborator")
|
||||
value(:create_comment, as: "create_comment")
|
||||
value(:integration_execute_azure_storage_container, as: "integration_execute_azure_storage_container")
|
||||
end
|
||||
|
||||
interface :project_integration do
|
||||
|
6
lib/hook/event.ex
Normal file
6
lib/hook/event.ex
Normal file
@ -0,0 +1,6 @@
|
||||
defmodule Accent.Hook.Event do
|
||||
@moduledoc false
|
||||
@callback name :: String.t()
|
||||
@callback triggered?(args :: map(), new_state :: map()) :: boolean()
|
||||
@callback payload(args :: map(), new_state :: map()) :: map()
|
||||
end
|
24
lib/hook/events.ex
Normal file
24
lib/hook/events.ex
Normal file
@ -0,0 +1,24 @@
|
||||
defmodule Accent.Hook.Events do
|
||||
@moduledoc false
|
||||
path_wildcard = Path.join(Path.dirname(__ENV__.file), "events/*.ex")
|
||||
paths = Path.wildcard(path_wildcard)
|
||||
paths_hash = :erlang.md5(paths)
|
||||
|
||||
@callback registered_events :: [String.t()] | :all
|
||||
|
||||
event_modules =
|
||||
for name <- paths do
|
||||
Module.safe_concat(__MODULE__, Phoenix.Naming.camelize(Path.basename(name, ".ex")))
|
||||
end
|
||||
|
||||
@event_modules event_modules
|
||||
|
||||
def available do
|
||||
@event_modules
|
||||
end
|
||||
|
||||
def __mix_recompile__? do
|
||||
paths = Path.wildcard(unquote(path_wildcard))
|
||||
:erlang.md5(paths) != unquote(paths_hash)
|
||||
end
|
||||
end
|
23
lib/hook/events/add_translations.ex
Normal file
23
lib/hook/events/add_translations.ex
Normal file
@ -0,0 +1,23 @@
|
||||
defmodule Accent.Hook.Events.AddTranslations do
|
||||
@moduledoc false
|
||||
@behaviour Accent.Hook.Event
|
||||
|
||||
alias Movement.Persisters.ProjectHookWorker
|
||||
|
||||
@impl true
|
||||
def name do
|
||||
"add_translations"
|
||||
end
|
||||
|
||||
@impl true
|
||||
def triggered?(%ProjectHookWorker.Args{} = args, _new_state) do
|
||||
args.batch_action === "merge" and args.operations_count > 0
|
||||
end
|
||||
|
||||
@impl true
|
||||
def payload(%ProjectHookWorker.Args{} = args, _new_state) do
|
||||
%{
|
||||
language_name: args.revision.language.name
|
||||
}
|
||||
end
|
||||
end
|
24
lib/hook/events/complete_review.ex
Normal file
24
lib/hook/events/complete_review.ex
Normal file
@ -0,0 +1,24 @@
|
||||
defmodule Accent.Hook.Events.CompleteReview do
|
||||
@moduledoc false
|
||||
@behaviour Accent.Hook.Event
|
||||
|
||||
alias Movement.Persisters.ProjectHookWorker
|
||||
|
||||
@impl true
|
||||
def name do
|
||||
"complete_review"
|
||||
end
|
||||
|
||||
@impl true
|
||||
def triggered?(%ProjectHookWorker.Args{} = args, %ProjectHookWorker.ProjectState{} = project_state) do
|
||||
args.previous_project_state.reviewed_count !== args.previous_project_state.translations_count and
|
||||
project_state.reviewed_count === project_state.translations_count
|
||||
end
|
||||
|
||||
@impl true
|
||||
def payload(_args, %ProjectHookWorker.ProjectState{} = project_state) do
|
||||
%{
|
||||
translations_count: project_state.translations_count
|
||||
}
|
||||
end
|
||||
end
|
25
lib/hook/events/new_conflicts.ex
Normal file
25
lib/hook/events/new_conflicts.ex
Normal file
@ -0,0 +1,25 @@
|
||||
defmodule Accent.Hook.Events.NewConflicts do
|
||||
@moduledoc false
|
||||
@behaviour Accent.Hook.Event
|
||||
|
||||
alias Movement.Persisters.ProjectHookWorker
|
||||
|
||||
@impl true
|
||||
def name do
|
||||
"new_conflicts"
|
||||
end
|
||||
|
||||
@impl true
|
||||
def triggered?(%ProjectHookWorker.Args{} = args, %ProjectHookWorker.ProjectState{} = project_state) do
|
||||
args.previous_project_state.conflicts_count < project_state.conflicts_count
|
||||
end
|
||||
|
||||
@impl true
|
||||
def payload(%ProjectHookWorker.Args{} = args, %ProjectHookWorker.ProjectState{} = project_state) do
|
||||
%{
|
||||
reviewed_count: project_state.reviewed_count,
|
||||
translations_count: project_state.translations_count,
|
||||
new_conflicts_count: project_state.conflicts_count - args.previous_project_state.conflicts_count
|
||||
}
|
||||
end
|
||||
end
|
24
lib/hook/events/sync.ex
Normal file
24
lib/hook/events/sync.ex
Normal file
@ -0,0 +1,24 @@
|
||||
defmodule Accent.Hook.Events.Sync do
|
||||
@moduledoc false
|
||||
@behaviour Accent.Hook.Event
|
||||
|
||||
alias Movement.Persisters.ProjectHookWorker
|
||||
|
||||
@impl true
|
||||
def name do
|
||||
"sync"
|
||||
end
|
||||
|
||||
@impl true
|
||||
def triggered?(%ProjectHookWorker.Args{} = args, _new_state) do
|
||||
args.batch_action === "sync" and args.operations_count > 0
|
||||
end
|
||||
|
||||
@impl true
|
||||
def payload(%ProjectHookWorker.Args{} = args, _new_state) do
|
||||
%{
|
||||
batch_operation_stats: args.batch_operation.stats,
|
||||
document_path: args.document.path
|
||||
}
|
||||
end
|
||||
end
|
@ -1,19 +1,17 @@
|
||||
defmodule Accent.Hook do
|
||||
@moduledoc false
|
||||
def outbound(context), do: run(outbounds_modules(), context)
|
||||
@outbounds_modules Application.compile_env!(:accent, __MODULE__)[:outbounds]
|
||||
|
||||
defp run(modules, context) do
|
||||
def outbound(context) do
|
||||
jobs =
|
||||
Enum.reduce(modules, [], fn {module, opts}, acc ->
|
||||
if context.event in Keyword.fetch!(opts, :events),
|
||||
do: [module.new(context) | acc],
|
||||
else: acc
|
||||
Enum.flat_map(@outbounds_modules, fn module ->
|
||||
events = module.registered_events()
|
||||
|
||||
if events === :all or context.event in events,
|
||||
do: [module.new(context)],
|
||||
else: []
|
||||
end)
|
||||
|
||||
Oban.insert_all(jobs)
|
||||
end
|
||||
|
||||
defp outbounds_modules do
|
||||
Application.get_env(:accent, __MODULE__)[:outbounds]
|
||||
end
|
||||
end
|
||||
|
50
lib/hook/outbounds/discord.ex
Normal file
50
lib/hook/outbounds/discord.ex
Normal file
@ -0,0 +1,50 @@
|
||||
defmodule Accent.Hook.Outbounds.Discord do
|
||||
@moduledoc false
|
||||
@behaviour Accent.Hook.Events
|
||||
|
||||
use Oban.Worker, queue: :hook
|
||||
|
||||
defmodule Templates do
|
||||
@moduledoc false
|
||||
import Accent.Hook.Outbounds.Helpers.StringTemplate
|
||||
|
||||
deftemplate(:new_conflicts, """
|
||||
**<%= @user %>** just added *<%= @new_conflicts_count %> strings* to review.
|
||||
The project is currently **<%= Float.round(@reviewed_count / @translations_count * 100, 2) %>** reviewed (<%= @reviewed_count %>/<%= @translations_count %>)
|
||||
""")
|
||||
|
||||
deftemplate(:sync, """
|
||||
**<%= @user %>** just synced a file: *<%= @document_path %>*
|
||||
|
||||
**Stats:**<%= for %{"action" => action, "count" => count} <- @stats do %>
|
||||
<%= Phoenix.Naming.humanize(action) %>: *<%= count %>*<% end %>
|
||||
""")
|
||||
|
||||
deftemplate(:complete_review, """
|
||||
**<%= @user %>** just finished reviewing all strings!
|
||||
The project currently has <%= @translations_count %> reviewed translations.
|
||||
""")
|
||||
|
||||
deftemplate(:integration_execute_azure_storage_container, """
|
||||
**<%= @user %>** just uploaded all *<%= @version_tag %>* files to Azure Container Storage.
|
||||
<%= for %{"name" => document_name, "url" => url} <- @document_urls do %>
|
||||
[<%= document_name %>](<%= url %>)
|
||||
<% end %>
|
||||
""")
|
||||
end
|
||||
|
||||
@impl Accent.Hook.Events
|
||||
def registered_events do
|
||||
~w(sync complete_review new_conflicts integration_execute_azure_storage_container)
|
||||
end
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(%Oban.Job{args: args}) do
|
||||
context = Accent.Hook.Context.from_worker(args)
|
||||
|
||||
Accent.Hook.Outbounds.Helpers.PostURL.perform("discord", context,
|
||||
http_body: &%{content: &1},
|
||||
templates: Templates
|
||||
)
|
||||
end
|
||||
end
|
@ -1,15 +0,0 @@
|
||||
defmodule Accent.Hook.Outbounds.Discord do
|
||||
@moduledoc false
|
||||
use Oban.Worker, queue: :hook
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(%Oban.Job{args: args}) do
|
||||
context = Accent.Hook.Context.from_worker(args)
|
||||
http_body = fn content -> %{content: content} end
|
||||
|
||||
Accent.Hook.Outbounds.PostURL.perform("discord", context,
|
||||
http_body: http_body,
|
||||
templates: Hook.Outbounds.Discord.Templates
|
||||
)
|
||||
end
|
||||
end
|
@ -1,23 +0,0 @@
|
||||
defmodule Hook.Outbounds.Discord.Templates do
|
||||
@moduledoc false
|
||||
require EEx
|
||||
|
||||
@sync_template """
|
||||
**<%= @user %>** just synced a file: *<%= @document_path %>*
|
||||
|
||||
**Stats:**<%= for %{"action" => action, "count" => count} <- @stats do %>
|
||||
<%= Phoenix.Naming.humanize(action) %>: *<%= count %>*<% end %>
|
||||
"""
|
||||
@new_conflicts_template """
|
||||
**<%= @user %>** just added *<%= @new_conflicts_count %> strings* to review.
|
||||
The project is currently **<%= Float.round(@reviewed_count / @translations_count * 100, 2) %>** reviewed (<%= @reviewed_count %>/<%= @translations_count %>)
|
||||
"""
|
||||
@complete_review_template """
|
||||
**<%= @user %>** just finished reviewing all strings!
|
||||
The project currently has <%= @translations_count %> reviewed translations.
|
||||
"""
|
||||
|
||||
EEx.function_from_string(:def, :sync, @sync_template, [:assigns], trim: true)
|
||||
EEx.function_from_string(:def, :new_conflicts, @new_conflicts_template, [:assigns], trim: true)
|
||||
EEx.function_from_string(:def, :complete_review, @complete_review_template, [:assigns], trim: true)
|
||||
end
|
@ -1,5 +1,7 @@
|
||||
defmodule Accent.Hook.Outbounds.Email do
|
||||
@moduledoc false
|
||||
@behaviour Accent.Hook.Events
|
||||
|
||||
use Oban.Worker, queue: :hook
|
||||
|
||||
alias Accent.CreateCommentEmail
|
||||
@ -7,6 +9,11 @@ defmodule Accent.Hook.Outbounds.Email do
|
||||
alias Accent.Repo
|
||||
alias Accent.Translation
|
||||
|
||||
@impl Accent.Hook.Events
|
||||
def registered_events do
|
||||
~w(create_collaborator create_comment)
|
||||
end
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(%Oban.Job{args: args}) do
|
||||
context = Accent.Hook.Context.from_worker(args)
|
||||
|
@ -1,4 +1,4 @@
|
||||
defmodule Accent.Hook.Outbounds.PostURL do
|
||||
defmodule Accent.Hook.Outbounds.Helpers.PostURL do
|
||||
@moduledoc false
|
||||
import Ecto.Query, only: [where: 2]
|
||||
|
||||
@ -58,27 +58,35 @@ defmodule Accent.Hook.Outbounds.PostURL do
|
||||
defp formatted_diff(diff) when diff > 1000, do: [diff |> div(1000) |> Integer.to_string(), "ms"]
|
||||
defp formatted_diff(diff), do: [Integer.to_string(diff), "µs"]
|
||||
|
||||
defp build_content(templates, %{event: "sync", user: user, payload: payload}) do
|
||||
defp build_content(templates, %{event: "sync"} = context) do
|
||||
templates.sync(%{
|
||||
user: User.name_with_fallback(user),
|
||||
document_path: payload["document_path"],
|
||||
stats: payload["batch_operation_stats"]
|
||||
user: User.name_with_fallback(context.user),
|
||||
document_path: context.payload["document_path"],
|
||||
stats: context.payload["batch_operation_stats"]
|
||||
})
|
||||
end
|
||||
|
||||
defp build_content(templates, %{event: "new_conflicts", user: user, payload: payload}) do
|
||||
defp build_content(templates, %{event: "new_conflicts"} = context) do
|
||||
templates.new_conflicts(%{
|
||||
user: User.name_with_fallback(user),
|
||||
reviewed_count: payload["reviewed_count"],
|
||||
new_conflicts_count: payload["new_conflicts_count"],
|
||||
translations_count: payload["translations_count"]
|
||||
user: User.name_with_fallback(context.user),
|
||||
reviewed_count: context.payload["reviewed_count"],
|
||||
new_conflicts_count: context.payload["new_conflicts_count"],
|
||||
translations_count: context.payload["translations_count"]
|
||||
})
|
||||
end
|
||||
|
||||
defp build_content(templates, %{event: "complete_review", user: user, payload: payload}) do
|
||||
defp build_content(templates, %{event: "complete_review"} = context) do
|
||||
templates.complete_review(%{
|
||||
user: User.name_with_fallback(user),
|
||||
translations_count: payload["translations_count"]
|
||||
user: User.name_with_fallback(context.user),
|
||||
translations_count: context.payload["translations_count"]
|
||||
})
|
||||
end
|
||||
|
||||
defp build_content(templates, %{event: "integration_execute_azure_storage_container"} = context) do
|
||||
templates.integration_execute_azure_storage_container(%{
|
||||
user: User.name_with_fallback(context.user),
|
||||
version_tag: context.payload["version_tag"],
|
||||
document_urls: context.payload["document_urls"]
|
||||
})
|
||||
end
|
||||
end
|
11
lib/hook/outbounds/helpers/string_template.ex
Normal file
11
lib/hook/outbounds/helpers/string_template.ex
Normal file
@ -0,0 +1,11 @@
|
||||
defmodule Accent.Hook.Outbounds.Helpers.StringTemplate do
|
||||
@moduledoc false
|
||||
|
||||
defmacro deftemplate(name, template) do
|
||||
quote do
|
||||
require EEx
|
||||
|
||||
EEx.function_from_string(:def, unquote(name), unquote(template), [:assigns], trim: true)
|
||||
end
|
||||
end
|
||||
end
|
50
lib/hook/outbounds/slack.ex
Normal file
50
lib/hook/outbounds/slack.ex
Normal file
@ -0,0 +1,50 @@
|
||||
defmodule Accent.Hook.Outbounds.Slack do
|
||||
@moduledoc false
|
||||
@behaviour Accent.Hook.Events
|
||||
|
||||
use Oban.Worker, queue: :hook
|
||||
|
||||
defmodule Templates do
|
||||
@moduledoc false
|
||||
import Accent.Hook.Outbounds.Helpers.StringTemplate
|
||||
|
||||
deftemplate(:new_conflicts, """
|
||||
*<%= @user %>* just added _<%= @new_conflicts_count %> strings_ to review.
|
||||
The project is currently *<%= Float.round(@reviewed_count / @translations_count * 100, 2) %>* reviewed (<%= @reviewed_count %>/<%= @translations_count %>)
|
||||
""")
|
||||
|
||||
deftemplate(:sync, """
|
||||
*<%= @user %>* just synced a file: _<%= @document_path %>_
|
||||
|
||||
*Stats:*<%= for %{"action" => action, "count" => count} <- @stats do %>
|
||||
<%= Phoenix.Naming.humanize(action) %>: _<%= count %>_<% end %>
|
||||
""")
|
||||
|
||||
deftemplate(:complete_review, """
|
||||
*<%= @user %>* just finished reviewing all strings!
|
||||
The project currently has <%= @translations_count %> reviewed translations.
|
||||
""")
|
||||
|
||||
deftemplate(:integration_execute_azure_storage_container, """
|
||||
*<%= @user %>* just uploaded all _<%= @version_tag %>_ files to Azure Container Storage.
|
||||
<%= for %{"name" => document_name, "url" => url} <- @document_urls do %>
|
||||
[<%= document_name %>](<%= url %>)
|
||||
<% end %>
|
||||
""")
|
||||
end
|
||||
|
||||
@impl Accent.Hook.Events
|
||||
def registered_events do
|
||||
~w(sync complete_review new_conflicts integration_execute_azure_storage_container)
|
||||
end
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(%Oban.Job{args: args}) do
|
||||
context = Accent.Hook.Context.from_worker(args)
|
||||
|
||||
Accent.Hook.Outbounds.Helpers.PostURL.perform("slack", context,
|
||||
http_body: &%{text: &1},
|
||||
templates: Templates
|
||||
)
|
||||
end
|
||||
end
|
@ -1,15 +0,0 @@
|
||||
defmodule Accent.Hook.Outbounds.Slack do
|
||||
@moduledoc false
|
||||
use Oban.Worker, queue: :hook
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(%Oban.Job{args: args}) do
|
||||
context = Accent.Hook.Context.from_worker(args)
|
||||
http_body = fn content -> %{text: content} end
|
||||
|
||||
Accent.Hook.Outbounds.PostURL.perform("slack", context,
|
||||
http_body: http_body,
|
||||
templates: Hook.Outbounds.Slack.Templates
|
||||
)
|
||||
end
|
||||
end
|
@ -1,23 +0,0 @@
|
||||
defmodule Hook.Outbounds.Slack.Templates do
|
||||
@moduledoc false
|
||||
require EEx
|
||||
|
||||
@sync_template """
|
||||
*<%= @user %>* just synced a file: _<%= @document_path %>_
|
||||
|
||||
*Stats:*<%= for %{"action" => action, "count" => count} <- @stats do %>
|
||||
<%= Phoenix.Naming.humanize(action) %>: _<%= count %>_<% end %>
|
||||
"""
|
||||
@new_conflicts_template """
|
||||
*<%= @user %>* just added _<%= @new_conflicts_count %> strings_ to review.
|
||||
The project is currently *<%= Float.round(@reviewed_count / @translations_count * 100, 2) %>* reviewed (<%= @reviewed_count %>/<%= @translations_count %>)
|
||||
"""
|
||||
@complete_review_template """
|
||||
*<%= @user %>* just finished reviewing all strings!
|
||||
The project currently has <%= @translations_count %> reviewed translations.
|
||||
"""
|
||||
|
||||
EEx.function_from_string(:def, :sync, @sync_template, [:assigns], trim: true)
|
||||
EEx.function_from_string(:def, :new_conflicts, @new_conflicts_template, [:assigns], trim: true)
|
||||
EEx.function_from_string(:def, :complete_review, @complete_review_template, [:assigns], trim: true)
|
||||
end
|
@ -1,7 +1,14 @@
|
||||
defmodule Accent.Hook.Outbounds.Websocket do
|
||||
@moduledoc false
|
||||
@behaviour Accent.Hook.Events
|
||||
|
||||
use Oban.Worker, queue: :hook
|
||||
|
||||
@impl Accent.Hook.Events
|
||||
def registered_events do
|
||||
~w(sync create_collaborator create_comment complete_review new_conflicts)
|
||||
end
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(%Oban.Job{args: args}) do
|
||||
args
|
||||
|
@ -4,7 +4,7 @@ defmodule Movement.Persisters.Base do
|
||||
alias Accent.Repo
|
||||
alias Movement.Mappers.OperationsStats, as: StatMapper
|
||||
alias Movement.Migrator
|
||||
alias Movement.Persisters.ProjectStateChangeWorker
|
||||
alias Movement.Persisters.ProjectHookWorker
|
||||
|
||||
require Ecto.Query
|
||||
|
||||
@ -39,7 +39,9 @@ defmodule Movement.Persisters.Base do
|
||||
end
|
||||
|
||||
def execute(context) do
|
||||
project_state_change_context = %{
|
||||
project_context = %{
|
||||
batch_action: context.assigns[:batch_action],
|
||||
operations_count: Enum.count(context.operations),
|
||||
project_id: context.assigns[:project] && context.assigns.project.id,
|
||||
document_id: context.assigns[:document] && context.assigns.document.id,
|
||||
master_revision_id: context.assigns[:master_revision] && context.assigns.master_revision.id,
|
||||
@ -47,15 +49,14 @@ defmodule Movement.Persisters.Base do
|
||||
version_id: context.assigns[:version] && context.assigns.version.id,
|
||||
batch_operation_id: context.assigns[:batch_operation] && context.assigns.batch_operation.id,
|
||||
user_id: context.assigns[:user_id],
|
||||
previous_project_state: ProjectStateChangeWorker.get_project_state(context.assigns[:project])
|
||||
previous_project_state: ProjectHookWorker.get_project_state(context.assigns[:project])
|
||||
}
|
||||
|
||||
context
|
||||
|> persist_operations()
|
||||
|> migrate_up_operations()
|
||||
|> tap(fn _ ->
|
||||
project_state_change_context.previous_project_state &&
|
||||
Oban.insert(ProjectStateChangeWorker.new(project_state_change_context))
|
||||
context.assigns[:project] && Oban.insert(ProjectHookWorker.new(project_context))
|
||||
end)
|
||||
end
|
||||
|
||||
|
92
lib/movement/persisters/project_hook_worker.ex
Normal file
92
lib/movement/persisters/project_hook_worker.ex
Normal file
@ -0,0 +1,92 @@
|
||||
defmodule Movement.Persisters.ProjectHookWorker do
|
||||
@moduledoc false
|
||||
use Oban.Worker, queue: :hook
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
alias Accent.Hook
|
||||
alias Accent.Repo
|
||||
alias Accent.Scopes.Project, as: ProjectScope
|
||||
|
||||
defmodule ProjectState do
|
||||
@moduledoc false
|
||||
@derive Jason.Encoder
|
||||
defstruct translations_count: 0, reviewed_count: 0, conflicts_count: 0
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
end
|
||||
|
||||
defmodule Args do
|
||||
@moduledoc false
|
||||
defstruct previous_project_state: %ProjectState{},
|
||||
operations_count: 0,
|
||||
project: nil,
|
||||
document: nil,
|
||||
master_revision: nil,
|
||||
revision: nil,
|
||||
version: nil,
|
||||
batch_operation: nil,
|
||||
batch_action: nil,
|
||||
user: nil
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
end
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(%Oban.Job{args: args}) do
|
||||
args = cast_args(args)
|
||||
current_project_state = get_project_state(args.project)
|
||||
|
||||
for module <- Accent.Hook.Events.available() do
|
||||
if module.triggered?(args, current_project_state) do
|
||||
Hook.outbound(%Hook.Context{
|
||||
event: module.name(),
|
||||
project_id: args.project.id,
|
||||
user_id: args.user && args.user.id,
|
||||
payload: module.payload(args, current_project_state)
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
def get_project_state(nil), do: nil
|
||||
|
||||
def get_project_state(project) do
|
||||
project =
|
||||
Accent.Project
|
||||
|> from(where: [id: ^project.id])
|
||||
|> ProjectScope.with_stats()
|
||||
|> Repo.one()
|
||||
|
||||
struct!(ProjectState, Map.take(project, ~w(translations_count reviewed_count conflicts_count)a))
|
||||
end
|
||||
|
||||
defp cast_args(args) do
|
||||
%Args{
|
||||
previous_project_state: cast_project_state(args["previous_project_state"]),
|
||||
project: get_record(Accent.Project, args["project_id"]),
|
||||
document: get_record(Accent.Document, args["document_id"]),
|
||||
master_revision: get_record(Accent.Revision, args["master_revision_id"]),
|
||||
revision: get_record(Accent.Revision, args["revision_id"]),
|
||||
version: get_record(Accent.Version, args["version_id"]),
|
||||
batch_operation: get_record(Accent.Operation, args["batch_operation_id"]),
|
||||
batch_action: args["batch_action"],
|
||||
user: get_record(Accent.User, args["user_id"])
|
||||
}
|
||||
end
|
||||
|
||||
defp cast_project_state(nil), do: nil
|
||||
|
||||
defp cast_project_state(args) do
|
||||
%ProjectState{
|
||||
translations_count: args["translations_count"],
|
||||
reviewed_count: args["reviewed_count"],
|
||||
conflicts_count: args["conflicts_count"]
|
||||
}
|
||||
end
|
||||
|
||||
defp get_record(_schema, nil), do: nil
|
||||
defp get_record(schema, id), do: Repo.get(schema, id)
|
||||
end
|
@ -1,90 +0,0 @@
|
||||
defmodule Movement.Persisters.ProjectStateChangeWorker do
|
||||
@moduledoc false
|
||||
use Oban.Worker, queue: :hook
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
alias Accent.Hook
|
||||
alias Accent.Repo
|
||||
alias Accent.Scopes.Project, as: ProjectScope
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(%Oban.Job{args: args}) do
|
||||
args = cast_args(args)
|
||||
current_project_state = get_project_state(args.project)
|
||||
|
||||
if new_conflicts_to_review?(args.previous_project_state, current_project_state) do
|
||||
Hook.outbound(%Hook.Context{
|
||||
event: "new_conflicts",
|
||||
project_id: args.project.id,
|
||||
user_id: args.user.id,
|
||||
payload: %{
|
||||
reviewed_count: current_project_state.project.reviewed_count,
|
||||
translations_count: current_project_state.project.translations_count,
|
||||
new_conflicts_count:
|
||||
current_project_state.project.conflicts_count - args.previous_project_state.project.conflicts_count
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
if all_reviewed?(args.previous_project_state, current_project_state) do
|
||||
Hook.outbound(%Hook.Context{
|
||||
event: "complete_review",
|
||||
project_id: args.project.id,
|
||||
user_id: args.user.id,
|
||||
payload: %{
|
||||
translations_count: current_project_state.project.translations_count
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
defp new_conflicts_to_review?(previous_state, current_state) do
|
||||
previous_state.project.conflicts_count < current_state.project.conflicts_count
|
||||
end
|
||||
|
||||
defp all_reviewed?(previous_state, current_state) do
|
||||
previous_state.project.reviewed_count !== previous_state.project.translations_count and
|
||||
current_state.project.reviewed_count === current_state.project.translations_count
|
||||
end
|
||||
|
||||
def get_project_state(nil), do: nil
|
||||
|
||||
def get_project_state(project) do
|
||||
project =
|
||||
Accent.Project
|
||||
|> from(where: [id: ^project.id])
|
||||
|> ProjectScope.with_stats()
|
||||
|> Repo.one()
|
||||
|
||||
%{project: Map.take(project, ~w(translations_count reviewed_count conflicts_count)a)}
|
||||
end
|
||||
|
||||
defp cast_project_state(args) do
|
||||
%{
|
||||
project: %{
|
||||
translations_count: args["project"]["translations_count"],
|
||||
reviewed_count: args["project"]["reviewed_count"],
|
||||
conflicts_count: args["project"]["conflicts_count"]
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp cast_args(args) do
|
||||
%{
|
||||
previous_project_state: cast_project_state(args["previous_project_state"]),
|
||||
project: get_record(Accent.Project, args["project_id"]),
|
||||
document: get_record(Accent.Document, args["document_id"]),
|
||||
master_revision: get_record(Accent.Revision, args["master_revision_id"]),
|
||||
revision: get_record(Accent.Revision, args["revision_id"]),
|
||||
version: get_record(Accent.Version, args["version_id"]),
|
||||
batch_operation: get_record(Accent.Operation, args["batch_operation_id"]),
|
||||
user: get_record(Accent.User, args["user_id"])
|
||||
}
|
||||
end
|
||||
|
||||
defp get_record(_schema, nil), do: nil
|
||||
defp get_record(schema, id), do: Repo.get(schema, id)
|
||||
end
|
@ -3,9 +3,9 @@ defmodule Accent.MergeController do
|
||||
|
||||
import Canary.Plugs
|
||||
|
||||
alias Accent.Hook.Context, as: HookContext
|
||||
alias Accent.Project
|
||||
alias Movement.Builders.RevisionMerge, as: RevisionMergeBuilder
|
||||
alias Movement.Context
|
||||
alias Movement.Persisters.RevisionMerge, as: RevisionMergePersister
|
||||
|
||||
plug(Plug.Assign, canary_action: :merge)
|
||||
@ -44,27 +44,14 @@ defmodule Accent.MergeController do
|
||||
"""
|
||||
def create(conn, _params) do
|
||||
conn.assigns[:movement_context]
|
||||
|> Movement.Context.assign(:revision, conn.assigns[:revision])
|
||||
|> Movement.Context.assign(:merge_type, conn.assigns[:merge_type])
|
||||
|> Movement.Context.assign(:options, conn.assigns[:merge_options])
|
||||
|> Movement.Context.assign(:user_id, conn.assigns[:current_user].id)
|
||||
|> Context.assign(:revision, conn.assigns[:revision])
|
||||
|> Context.assign(:merge_type, conn.assigns[:merge_type])
|
||||
|> Context.assign(:options, conn.assigns[:merge_options])
|
||||
|> Context.assign(:user_id, conn.assigns[:current_user].id)
|
||||
|> RevisionMergeBuilder.build()
|
||||
|> RevisionMergePersister.persist()
|
||||
|> case do
|
||||
{:ok, {_context, []}} ->
|
||||
send_resp(conn, :ok, "")
|
||||
|
||||
{:ok, _} ->
|
||||
Accent.Hook.outbound(%HookContext{
|
||||
event: "add_translations",
|
||||
project_id: conn.assigns[:project].id,
|
||||
user_id: conn.assigns[:current_user].id,
|
||||
payload: %{
|
||||
merge_type: conn.assigns[:merge_type],
|
||||
language_name: conn.assigns[:revision].language.name
|
||||
}
|
||||
})
|
||||
|
||||
{:ok, {_context, _}} ->
|
||||
send_resp(conn, :ok, "")
|
||||
|
||||
{:error, _reason} ->
|
||||
@ -74,7 +61,7 @@ defmodule Accent.MergeController do
|
||||
|
||||
defp assign_comparer(conn, _) do
|
||||
comparer = Movement.Comparer.comparer(:merge, conn.params["merge_type"])
|
||||
context = Movement.Context.assign(conn.assigns[:movement_context], :comparer, comparer)
|
||||
context = Context.assign(conn.assigns[:movement_context], :comparer, comparer)
|
||||
|
||||
assign(conn, :movement_context, context)
|
||||
end
|
||||
|
@ -3,9 +3,9 @@ defmodule Accent.SyncController do
|
||||
|
||||
import Canary.Plugs
|
||||
|
||||
alias Accent.Hook.Context, as: HookContext
|
||||
alias Accent.Project
|
||||
alias Movement.Builders.ProjectSync, as: SyncBuilder
|
||||
alias Movement.Context
|
||||
alias Movement.Persisters.ProjectSync, as: SyncPersister
|
||||
|
||||
plug(Plug.Assign, canary_action: :sync)
|
||||
@ -41,25 +41,12 @@ defmodule Accent.SyncController do
|
||||
"""
|
||||
def create(conn, _) do
|
||||
conn.assigns[:movement_context]
|
||||
|> Movement.Context.assign(:project, conn.assigns[:project])
|
||||
|> Movement.Context.assign(:user_id, conn.assigns[:current_user].id)
|
||||
|> Context.assign(:project, conn.assigns[:project])
|
||||
|> Context.assign(:user_id, conn.assigns[:current_user].id)
|
||||
|> SyncBuilder.build()
|
||||
|> SyncPersister.persist()
|
||||
|> case do
|
||||
{:ok, {_context, []}} ->
|
||||
send_resp(conn, :ok, "")
|
||||
|
||||
{:ok, {context, _operations}} ->
|
||||
Accent.Hook.outbound(%HookContext{
|
||||
event: "sync",
|
||||
project_id: conn.assigns[:project].id,
|
||||
user_id: conn.assigns[:current_user].id,
|
||||
payload: %{
|
||||
batch_operation_stats: context.assigns[:batch_operation].stats,
|
||||
document_path: context.assigns[:document].path
|
||||
}
|
||||
})
|
||||
|
||||
{:ok, {_context, _}} ->
|
||||
send_resp(conn, :ok, "")
|
||||
|
||||
{:error, _reason} ->
|
||||
@ -69,7 +56,7 @@ defmodule Accent.SyncController do
|
||||
|
||||
defp assign_comparer(conn, _) do
|
||||
comparer = Movement.Comparer.comparer(:sync, conn.params["sync_type"])
|
||||
context = Movement.Context.assign(conn.assigns[:movement_context], :comparer, comparer)
|
||||
context = Context.assign(conn.assigns[:movement_context], :comparer, comparer)
|
||||
|
||||
assign(conn, :movement_context, context)
|
||||
end
|
||||
|
@ -31,13 +31,4 @@ defmodule AccentTest.Hook do
|
||||
args: %{worker_args | "event" => "sync"}
|
||||
)
|
||||
end
|
||||
|
||||
test "unsupported event", %{context: context, worker_args: worker_args} do
|
||||
Hook.outbound(%{context | event: "foobar"})
|
||||
|
||||
refute_enqueued(
|
||||
worker: Hook.Outbounds.Mock,
|
||||
args: %{worker_args | "event" => "foobar"}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@ -1,4 +1,4 @@
|
||||
defmodule Movement.Persisters.ProjectStateChangeWorkerTest do
|
||||
defmodule Movement.Persisters.ProjectHookWorkerTest do
|
||||
use Accent.RepoCase, async: true
|
||||
|
||||
alias Accent.Document
|
||||
@ -7,7 +7,7 @@ defmodule Movement.Persisters.ProjectStateChangeWorkerTest do
|
||||
alias Accent.Repo
|
||||
alias Accent.Translation
|
||||
alias Accent.User
|
||||
alias Movement.Persisters.ProjectStateChangeWorker, as: Worker
|
||||
alias Movement.Persisters.ProjectHookWorker, as: Worker
|
||||
|
||||
setup do
|
||||
user = Repo.insert!(%User{email: "test@test.com"})
|
||||
@ -26,11 +26,9 @@ defmodule Movement.Persisters.ProjectStateChangeWorkerTest do
|
||||
args = %{
|
||||
"project_id" => project.id,
|
||||
"previous_project_state" => %{
|
||||
"project" => %{
|
||||
"translations_count" => 0,
|
||||
"reviewed_count" => 0,
|
||||
"conflicts_count" => 0
|
||||
}
|
||||
"translations_count" => 0,
|
||||
"reviewed_count" => 0,
|
||||
"conflicts_count" => 0
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,11 +51,9 @@ defmodule Movement.Persisters.ProjectStateChangeWorkerTest do
|
||||
"project_id" => project.id,
|
||||
"user_id" => user.id,
|
||||
"previous_project_state" => %{
|
||||
"project" => %{
|
||||
"translations_count" => 0,
|
||||
"reviewed_count" => 0,
|
||||
"conflicts_count" => 0
|
||||
}
|
||||
"translations_count" => 0,
|
||||
"reviewed_count" => 0,
|
||||
"conflicts_count" => 0
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,11 +88,9 @@ defmodule Movement.Persisters.ProjectStateChangeWorkerTest do
|
||||
"project_id" => project.id,
|
||||
"user_id" => user.id,
|
||||
"previous_project_state" => %{
|
||||
"project" => %{
|
||||
"translations_count" => 1,
|
||||
"reviewed_count" => 0,
|
||||
"conflicts_count" => 1
|
||||
}
|
||||
"translations_count" => 1,
|
||||
"reviewed_count" => 0,
|
||||
"conflicts_count" => 1
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,14 @@
|
||||
defmodule Accent.Hook.Outbounds.Mock do
|
||||
@moduledoc false
|
||||
@behaviour Accent.Hook.Events
|
||||
|
||||
use Oban.Worker, queue: :hook
|
||||
|
||||
@impl Accent.Hook.Events
|
||||
def registered_events do
|
||||
:all
|
||||
end
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(_job) do
|
||||
:ok
|
||||
|
@ -66,15 +66,7 @@ defmodule AccentTest.MergeController do
|
||||
|
||||
assert response.status == 200
|
||||
|
||||
assert_enqueued(
|
||||
worker: Accent.Hook.Outbounds.Mock,
|
||||
args: %{
|
||||
"event" => "add_translations",
|
||||
"payload" => %{"language_name" => "french", "merge_type" => nil},
|
||||
"project_id" => project.id,
|
||||
"user_id" => user.id
|
||||
}
|
||||
)
|
||||
assert_enqueued(worker: Movement.Persisters.ProjectHookWorker)
|
||||
|
||||
merge_on_proposed_operation = Repo.one(from(o in Operation, where: [action: ^"merge_on_proposed"]))
|
||||
merge_operation = Repo.one(from(o in Operation, where: [action: ^"merge"]))
|
||||
@ -160,15 +152,7 @@ defmodule AccentTest.MergeController do
|
||||
|
||||
assert response.status == 200
|
||||
|
||||
assert_enqueued(
|
||||
worker: Accent.Hook.Outbounds.Mock,
|
||||
args: %{
|
||||
"event" => "add_translations",
|
||||
"payload" => %{"language_name" => "french", "merge_type" => nil},
|
||||
"project_id" => project.id,
|
||||
"user_id" => user.id
|
||||
}
|
||||
)
|
||||
assert_enqueued(worker: Movement.Persisters.ProjectHookWorker)
|
||||
|
||||
merge_on_corrected_force_operation = Repo.one(from(o in Operation, where: [action: ^"merge_on_corrected_force"]))
|
||||
merge_operation = Repo.one(from(o in Operation, where: [action: ^"merge"]))
|
||||
@ -213,15 +197,7 @@ defmodule AccentTest.MergeController do
|
||||
|
||||
assert response.status == 200
|
||||
|
||||
assert_enqueued(
|
||||
worker: Accent.Hook.Outbounds.Mock,
|
||||
args: %{
|
||||
"event" => "add_translations",
|
||||
"payload" => %{"language_name" => "french", "merge_type" => nil},
|
||||
"project_id" => project.id,
|
||||
"user_id" => user.id
|
||||
}
|
||||
)
|
||||
assert_enqueued(worker: Movement.Persisters.ProjectHookWorker)
|
||||
|
||||
merge_on_proposed_operation = Repo.one(from(o in Operation, where: [action: ^"merge_on_proposed"]))
|
||||
merge_operation = Repo.one(from(o in Operation, where: [action: ^"merge"]))
|
||||
|
@ -52,18 +52,7 @@ defmodule AccentTest.SyncController do
|
||||
|
||||
assert response.status == 200
|
||||
|
||||
assert_enqueued(
|
||||
worker: Accent.Hook.Outbounds.Mock,
|
||||
args: %{
|
||||
"event" => "sync",
|
||||
"payload" => %{
|
||||
"batch_operation_stats" => [%{"action" => "new", "count" => 3}],
|
||||
"document_path" => "simple"
|
||||
},
|
||||
"project_id" => project.id,
|
||||
"user_id" => user.id
|
||||
}
|
||||
)
|
||||
assert_enqueued(worker: Movement.Persisters.ProjectHookWorker)
|
||||
|
||||
assert Enum.map(Repo.all(Document), &Map.get(&1, :path)) == ["simple"]
|
||||
|
||||
|
@ -51,10 +51,6 @@ export default class ConflictsList extends Component<Args> {
|
||||
);
|
||||
}
|
||||
|
||||
get showRevisionsHeader() {
|
||||
return this.revisions.length > 1;
|
||||
}
|
||||
|
||||
@action
|
||||
handleFocus(id: string) {
|
||||
this.selectedTranslationId = id;
|
||||
|
@ -29,6 +29,11 @@ export default class DataControlCheckboxes extends Component<Args> {
|
||||
label:
|
||||
'components.project_settings.integrations.events.options.complete_review',
|
||||
},
|
||||
{
|
||||
value: 'INTEGRATION_EXECUTE_AZURE_STORAGE_CONTAINER',
|
||||
label:
|
||||
'components.project_settings.integrations.events.options.integration_execute_azure_storage_container',
|
||||
},
|
||||
];
|
||||
|
||||
@tracked
|
||||
|
@ -532,7 +532,8 @@
|
||||
"options": {
|
||||
"sync": "Sync with any changes",
|
||||
"new_conflicts": "New strings to review",
|
||||
"complete_review": "Project is 100% reviewed"
|
||||
"complete_review": "Project is 100% reviewed",
|
||||
"integration_execute_azure_storage_container": "Files uploaded to Azure Storage Container"
|
||||
}
|
||||
},
|
||||
"execute": {
|
||||
|
@ -549,7 +549,8 @@
|
||||
"options": {
|
||||
"sync": "Synchroniser avec n’importe quelles modifications",
|
||||
"new_conflicts": "Nouvelles chaîne à réviser",
|
||||
"complete_review": "Le projet est revu à 100%"
|
||||
"complete_review": "Le projet est revu à 100%",
|
||||
"integration_execute_azure_storage_container": "Fichiers téléversés sur Azure Storage Container"
|
||||
}
|
||||
},
|
||||
"execute": {
|
||||
|
@ -15,11 +15,7 @@ export default class ApolloMutate extends Service {
|
||||
const operationName = Object.keys(data)[0];
|
||||
|
||||
if (!data[operationName]?.errors?.length) {
|
||||
const updatedData = {
|
||||
data,
|
||||
[operationName]: {...data[operationName], errors: null},
|
||||
};
|
||||
return updatedData;
|
||||
return {...data[operationName], errors: null};
|
||||
}
|
||||
|
||||
return data[operationName];
|
||||
|
@ -6,18 +6,16 @@
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.showRevisionsHeader}}
|
||||
<ul local-class='conflicts-header' style='--group-columns-count: {{this.revisions.length}};'>
|
||||
{{#each this.mappedRevisions as |revision|}}
|
||||
<li local-class='conflicts-header-item'>
|
||||
{{revision.name}}
|
||||
<span local-class='conflicts-header-item-slug'>
|
||||
{{revision.slug}}
|
||||
</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/if}}
|
||||
<ul local-class='conflicts-header' style='--group-columns-count: {{this.revisions.length}};'>
|
||||
{{#each this.mappedRevisions as |revision|}}
|
||||
<li local-class='conflicts-header-item'>
|
||||
{{revision.name}}
|
||||
<span local-class='conflicts-header-item-slug'>
|
||||
{{revision.slug}}
|
||||
</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
||||
<ul local-class='conflicts-items' style='--group-columns-count: {{this.revisions.length}};'>
|
||||
{{#each @groupedTranslations key='id' as |groupedTranslation index|}}
|
||||
|
Loading…
Reference in New Issue
Block a user