mirror of
https://github.com/mirego/accent.git
synced 2024-08-16 14:30:38 +03:00
Remove inbound GitHub hook in favor of using GitHub Actions to sync/commit
This commit is contained in:
parent
656dc7459c
commit
390bf62e35
@ -15,9 +15,7 @@ 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}],
|
||||
inbounds: [{Accent.Hook.Inbounds.Mock, events: events}]
|
||||
config :accent, Accent.Hook, outbounds: [{Accent.Hook.Outbounds.Mock, events: events}]
|
||||
else
|
||||
config :accent, Accent.Hook,
|
||||
outbounds: [
|
||||
@ -26,16 +24,13 @@ else
|
||||
{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)}
|
||||
],
|
||||
inbounds: [{Accent.Hook.Inbounds.GitHub, events: ~w(sync)}]
|
||||
]
|
||||
end
|
||||
|
||||
config :accent, Accent.Endpoint,
|
||||
render_errors: [accepts: ~w(json)],
|
||||
pubsub_server: Accent.PubSub
|
||||
|
||||
config :accent, hook_github_file_server: Accent.Hook.Inbounds.GitHub.FileServer.HTTP
|
||||
|
||||
config :accent, Oban,
|
||||
plugins: [Oban.Plugins.Pruner],
|
||||
queues: [hook: 10, operations: 10],
|
||||
|
@ -4,8 +4,6 @@ config :accent, Accent.Repo,
|
||||
pool: Ecto.Adapters.SQL.Sandbox,
|
||||
url: System.get_env("DATABASE_URL") || "postgres://localhost/accent_test"
|
||||
|
||||
config :accent, hook_github_file_server: Accent.Hook.Inbounds.GitHub.FileServerMock
|
||||
|
||||
config :ueberauth, Ueberauth, providers: [{:dummy, {Accent.Auth.Ueberauth.DummyStrategy, []}}]
|
||||
|
||||
config :accent, Oban, crontab: false, testing: :manual
|
||||
|
@ -80,14 +80,6 @@ defmodule Accent.IntegrationManager do
|
||||
end
|
||||
end
|
||||
|
||||
defp changeset_data("github") do
|
||||
fn model, params ->
|
||||
model
|
||||
|> cast(params, [:repository, :default_ref, :token])
|
||||
|> validate_required([:repository, :default_ref, :token])
|
||||
end
|
||||
end
|
||||
|
||||
defp changeset_data("azure_storage_container") do
|
||||
fn model, params ->
|
||||
model
|
||||
|
@ -9,9 +9,6 @@ defmodule Accent.Integration do
|
||||
|
||||
embeds_one(:data, IntegrationData, on_replace: :update) do
|
||||
field(:url)
|
||||
field(:repository)
|
||||
field(:token)
|
||||
field(:default_ref)
|
||||
field(:azure_storage_container_sas)
|
||||
end
|
||||
|
||||
|
@ -23,15 +23,4 @@ defmodule Accent.Scopes.Integration do
|
||||
def from_service(query, service) do
|
||||
from(query, where: [service: ^service])
|
||||
end
|
||||
|
||||
@doc """
|
||||
## Examples
|
||||
|
||||
iex> Accent.Scopes.Integration.from_data_repository(Accent.Integration, "test")
|
||||
#Ecto.Query<from i0 in Accent.Integration, where: fragment(\"?->>'repository' = ?\", i0.data, ^\"test\")>
|
||||
"""
|
||||
@spec from_data_repository(Ecto.Queryable.t(), String.t()) :: Ecto.Queryable.t()
|
||||
def from_data_repository(query, repository) do
|
||||
from(i in query, where: fragment("?->>'repository' = ?", i.data, ^repository))
|
||||
end
|
||||
end
|
||||
|
@ -20,9 +20,6 @@ defmodule Accent.GraphQL.Mutations.Integration do
|
||||
input_object :project_integration_data_input do
|
||||
field(:id, :id)
|
||||
field(:url, :string)
|
||||
field(:repository, :string)
|
||||
field(:default_ref, :string)
|
||||
field(:token, :string)
|
||||
field(:azure_storage_container_sas, :string)
|
||||
end
|
||||
|
||||
|
@ -72,8 +72,6 @@ defmodule Accent.GraphQL.Types.Integration do
|
||||
|
||||
object :project_integration_github_data do
|
||||
field(:id, non_null(:id))
|
||||
field(:repository, non_null(:string))
|
||||
field(:default_ref, non_null(:string))
|
||||
end
|
||||
|
||||
object :project_integration_azure_data do
|
||||
|
@ -1,7 +1,6 @@
|
||||
defmodule Accent.Hook do
|
||||
@moduledoc false
|
||||
def outbound(context), do: run(outbounds_modules(), context)
|
||||
def inbound(context), do: run(inbounds_modules(), context)
|
||||
|
||||
defp run(modules, context) do
|
||||
jobs =
|
||||
@ -17,8 +16,4 @@ defmodule Accent.Hook do
|
||||
defp outbounds_modules do
|
||||
Application.get_env(:accent, __MODULE__)[:outbounds]
|
||||
end
|
||||
|
||||
defp inbounds_modules do
|
||||
Application.get_env(:accent, __MODULE__)[:inbounds]
|
||||
end
|
||||
end
|
||||
|
@ -1,128 +0,0 @@
|
||||
defmodule Accent.Hook.Inbounds.GitHub do
|
||||
@moduledoc false
|
||||
use Oban.Worker, queue: :hook
|
||||
|
||||
alias Accent.Document
|
||||
alias Accent.Hook.Inbounds.GitHub.AddTranslations
|
||||
alias Accent.Hook.Inbounds.GitHub.Sync
|
||||
alias Accent.Plugs.MovementContextParser
|
||||
alias Accent.Repo
|
||||
alias Accent.Scopes.Document, as: DocumentScope
|
||||
alias Accent.Scopes.Version, as: VersionScope
|
||||
alias Accent.Version
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(%Oban.Job{args: args}) do
|
||||
context = Accent.Hook.Context.from_worker(args)
|
||||
|
||||
context.payload["ref"]
|
||||
|> ref_to_version(context.payload["default_ref"], context.project)
|
||||
|> sync_and_add_translations(context.project, context.user, context.payload)
|
||||
end
|
||||
|
||||
defp sync_and_add_translations({:ok, version}, project, user, payload) do
|
||||
repo = payload["repository"]
|
||||
token = payload["token"]
|
||||
|
||||
ref = (version && version.tag) || payload["default_ref"]
|
||||
configs = fetch_config(repo, token, ref)
|
||||
trees = fetch_trees(repo, token, ref)
|
||||
|
||||
revisions =
|
||||
project
|
||||
|> Ecto.assoc(:target_revisions)
|
||||
|> Repo.all()
|
||||
|> Repo.preload(:language)
|
||||
|
||||
Sync.persist(trees, configs, project, user, token, version)
|
||||
|
||||
Enum.each(revisions, fn revision ->
|
||||
AddTranslations.persist(trees, configs, project, user, revision, token, version)
|
||||
end)
|
||||
end
|
||||
|
||||
defp sync_and_add_translations(_, _, _, _), do: :ok
|
||||
|
||||
def filter_by_patterns(patterns, files) do
|
||||
Enum.group_by(files, fn file -> Enum.find(patterns, &ExMinimatch.match(&1["matcher"], file["path"])) end)
|
||||
end
|
||||
|
||||
def movement_document(project, path) do
|
||||
path =
|
||||
path
|
||||
|> Path.basename()
|
||||
|> MovementContextParser.extract_path_from_filename()
|
||||
|
||||
Document
|
||||
|> DocumentScope.from_path(path)
|
||||
|> DocumentScope.from_project(project.id)
|
||||
|> Repo.one()
|
||||
|> Kernel.||(%Document{project_id: project.id, path: path})
|
||||
end
|
||||
|
||||
def fetch_content(path, token) do
|
||||
with {:ok, %{body: %{"content" => content}}} <- file_server().get_path(path, headers(token)),
|
||||
decoded_contents =
|
||||
content
|
||||
|> String.split("\n")
|
||||
|> Enum.reject(&(&1 === ""))
|
||||
|> Enum.map(&Base.decode64/1),
|
||||
true <- Enum.all?(decoded_contents, &match?({:ok, _}, &1)) do
|
||||
decoded_content = Enum.map_join(decoded_contents, "", &elem(&1, 1))
|
||||
{:ok, decoded_content}
|
||||
else
|
||||
_ -> {:ok, nil}
|
||||
end
|
||||
end
|
||||
|
||||
def ref_to_version(ref, default_ref, project) do
|
||||
default_version(ref, default_ref) || version_from_ref(ref, project)
|
||||
end
|
||||
|
||||
defp default_version(ref, default_ref) do
|
||||
case Regex.named_captures(~r/refs\/heads\/(?<branch>.+)/, ref) do
|
||||
%{"branch" => ^default_ref} -> {:ok, nil}
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
defp version_from_ref(ref, project) do
|
||||
with %{"tag" => tag} <- Regex.named_captures(~r/refs\/tags\/(?<tag>.+)/, ref),
|
||||
%Version{} = version <-
|
||||
Version
|
||||
|> VersionScope.from_project(project.id)
|
||||
|> VersionScope.from_tag(tag)
|
||||
|> Repo.one() do
|
||||
{:ok, version}
|
||||
else
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
defp fetch_config(repo, token, ref) do
|
||||
path = Path.join([repo, "contents", "accent.json"]) <> "?ref=#{ref}"
|
||||
|
||||
with {:ok, config} when is_binary(config) <- fetch_content(path, token),
|
||||
{:ok, %{"files" => files}} <- Jason.decode(config) do
|
||||
files
|
||||
else
|
||||
_ -> []
|
||||
end
|
||||
end
|
||||
|
||||
defp fetch_trees(repo, token, ref) do
|
||||
path = Path.join([repo, "git", "trees", ref]) <> "?recursive=1"
|
||||
|
||||
case file_server().get_path(path, headers(token)) do
|
||||
{:ok, %{body: %{"tree" => tree}}} -> Enum.filter(tree, &(&1["type"] === "blob"))
|
||||
_ -> []
|
||||
end
|
||||
end
|
||||
|
||||
defp headers(token) do
|
||||
[{"Authorization", "token #{token}"}]
|
||||
end
|
||||
|
||||
defp file_server, do: Application.get_env(:accent, :hook_github_file_server)
|
||||
end
|
@ -1,72 +0,0 @@
|
||||
defmodule Accent.Hook.Inbounds.GitHub.AddTranslations do
|
||||
@moduledoc false
|
||||
alias Accent.Hook.Inbounds.GitHub
|
||||
alias Accent.Plugs.MovementContextParser
|
||||
alias Movement.Builders.RevisionMerge, as: RevisionMergeBuilder
|
||||
alias Movement.Context
|
||||
alias Movement.Persisters.RevisionMerge, as: RevisionMergePersister
|
||||
|
||||
def persist(trees, configs, project, user, revision, token, version) do
|
||||
trees
|
||||
|> group_by_matched_target_config(configs, revision)
|
||||
|> Enum.reject(&(elem(&1, 0) === nil))
|
||||
|> Enum.flat_map(fn {config, files} ->
|
||||
Enum.map(files, &build_context(&1, project, token, config["format"]))
|
||||
end)
|
||||
|> Enum.reject(&is_nil/1)
|
||||
|> Enum.map(&assign_defaults(&1, user, revision, project, version))
|
||||
|> Enum.each(&persist_contexts/1)
|
||||
end
|
||||
|
||||
defp group_by_matched_target_config(files, configs, revision) do
|
||||
configs
|
||||
|> Enum.map(fn config ->
|
||||
target =
|
||||
config["target"]
|
||||
|> Kernel.||("")
|
||||
|> String.replace("%slug%", Accent.Revision.language(revision).slug)
|
||||
|> String.replace("%original_file_name%", "*")
|
||||
|> String.replace("%document_path%", "*")
|
||||
|
||||
Map.put(config, "matcher", ExMinimatch.compile(target))
|
||||
end)
|
||||
|> GitHub.filter_by_patterns(files)
|
||||
end
|
||||
|
||||
defp persist_contexts(context) do
|
||||
context
|
||||
|> RevisionMergeBuilder.build()
|
||||
|> RevisionMergePersister.persist()
|
||||
end
|
||||
|
||||
defp assign_defaults(context, user, revision, project, version) do
|
||||
context
|
||||
|> Context.assign(:user_id, user.id)
|
||||
|> Context.assign(:revision, revision)
|
||||
|> Context.assign(:project, project)
|
||||
|> Context.assign(:version, version)
|
||||
|> Context.assign(:merge_type, "smart")
|
||||
|> Context.assign(:merge_options, [])
|
||||
|> Context.assign(:comparer, Movement.Comparer.comparer(:merge, "smart"))
|
||||
end
|
||||
|
||||
defp build_context(file, project, token, format) do
|
||||
with {:ok, parser} <- Langue.parser_from_format(format),
|
||||
%{id: id} = document when not is_nil(id) <- GitHub.movement_document(project, file["path"]),
|
||||
document = %{document | format: format},
|
||||
{:ok, file_content} <- GitHub.fetch_content(file["url"], token),
|
||||
%{entries: entries} <- MovementContextParser.to_entries(document, file_content, parser) do
|
||||
%Context{
|
||||
render: file_content,
|
||||
entries: entries,
|
||||
assigns: %{
|
||||
options: [],
|
||||
document: document,
|
||||
document_update: %{}
|
||||
}
|
||||
}
|
||||
else
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
end
|
@ -1,4 +0,0 @@
|
||||
defmodule Accent.Hook.Inbounds.GitHub.FileServer do
|
||||
@moduledoc false
|
||||
@callback get_path(String.t(), list()) :: {:ok, String.t()} | {:error, any()}
|
||||
end
|
@ -1,25 +0,0 @@
|
||||
defmodule Accent.Hook.Inbounds.GitHub.FileServer.HTTP do
|
||||
@moduledoc false
|
||||
@behaviour Accent.Hook.Inbounds.GitHub.FileServer
|
||||
|
||||
use HTTPoison.Base
|
||||
|
||||
@base_url "https://api.github.com/repos/"
|
||||
|
||||
@impl true
|
||||
def get_path(path, options), do: get(path, options)
|
||||
|
||||
@impl true
|
||||
def process_url(@base_url <> path), do: process_url(path)
|
||||
def process_url(path), do: @base_url <> path
|
||||
|
||||
@impl true
|
||||
def process_response_body(body) do
|
||||
body
|
||||
|> Jason.decode()
|
||||
|> case do
|
||||
{:ok, body} -> body
|
||||
_ -> :error
|
||||
end
|
||||
end
|
||||
end
|
@ -1,67 +0,0 @@
|
||||
defmodule Accent.Hook.Inbounds.GitHub.Sync do
|
||||
@moduledoc false
|
||||
alias Accent.Hook.Inbounds.GitHub
|
||||
alias Accent.Plugs.MovementContextParser
|
||||
alias Movement.Builders.ProjectSync, as: SyncBuilder
|
||||
alias Movement.Context
|
||||
alias Movement.Persisters.ProjectSync, as: SyncPersister
|
||||
|
||||
def persist(trees, configs, project, user, token, version) do
|
||||
trees
|
||||
|> group_by_matched_source_config(configs)
|
||||
|> Enum.reject(&(elem(&1, 0) === nil))
|
||||
|> Enum.flat_map(fn {config, files} ->
|
||||
Enum.map(files, &build_context(&1, project, token, config["format"]))
|
||||
end)
|
||||
|> Enum.reject(&is_nil/1)
|
||||
|> Enum.map(&assign_defaults(&1, user, project, version))
|
||||
|> Enum.each(&persist_contexts/1)
|
||||
end
|
||||
|
||||
defp group_by_matched_source_config(files, configs) do
|
||||
configs
|
||||
|> Enum.map(&Map.put(&1, "matcher", ExMinimatch.compile(&1["source"])))
|
||||
|> GitHub.filter_by_patterns(files)
|
||||
end
|
||||
|
||||
defp persist_contexts(context) do
|
||||
context
|
||||
|> SyncBuilder.build()
|
||||
|> SyncPersister.persist()
|
||||
end
|
||||
|
||||
defp assign_defaults(context, user, project, version) do
|
||||
context
|
||||
|> Context.assign(:user_id, user.id)
|
||||
|> Context.assign(:project, project)
|
||||
|> Context.assign(:version, version)
|
||||
|> Context.assign(:comparer, Movement.Comparer.comparer(:sync, "smart"))
|
||||
end
|
||||
|
||||
defp build_context(file, project, token, format) do
|
||||
with {:ok, parser} <- Langue.parser_from_format(format),
|
||||
document = GitHub.movement_document(project, file["path"]),
|
||||
document = %{document | format: format},
|
||||
{:ok, file_content} <- GitHub.fetch_content(file["url"], token),
|
||||
%{entries: entries, document: parsed_document} <-
|
||||
MovementContextParser.to_entries(document, file_content, parser) do
|
||||
%Context{
|
||||
render: file_content,
|
||||
entries: entries,
|
||||
assigns: %{
|
||||
options: [],
|
||||
document: document,
|
||||
document_update: document_update(parsed_document)
|
||||
}
|
||||
}
|
||||
else
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
defp document_update(nil), do: nil
|
||||
|
||||
defp document_update(parsed_document) do
|
||||
%{top_of_the_file_comment: parsed_document.top_of_the_file_comment, header: parsed_document.header}
|
||||
end
|
||||
end
|
@ -1,76 +0,0 @@
|
||||
defmodule Accent.Hook.GitHubController do
|
||||
use Plug.Builder
|
||||
|
||||
import Canary.Plugs
|
||||
import Ecto.Query, only: [first: 1]
|
||||
|
||||
alias Accent.Hook.Context, as: HookContext
|
||||
alias Accent.Integration
|
||||
alias Accent.Project
|
||||
alias Accent.Repo
|
||||
alias Accent.Scopes.Integration, as: IntegrationScope
|
||||
|
||||
plug(Plug.Assign, canary_action: :hook_update)
|
||||
plug(:load_and_authorize_resource, model: Project, id_name: "project_id")
|
||||
plug(:filter_event_type)
|
||||
plug(:assign_payload)
|
||||
plug(:update)
|
||||
|
||||
def update(conn, _) do
|
||||
Accent.Hook.inbound(%HookContext{
|
||||
event: "sync",
|
||||
payload: conn.assigns[:payload],
|
||||
project_id: conn.assigns[:project].id,
|
||||
user_id: conn.assigns[:current_user].id
|
||||
})
|
||||
|
||||
send_resp(conn, :no_content, "")
|
||||
end
|
||||
|
||||
defp assign_payload(conn, _) do
|
||||
with repository when is_binary(repository) <- conn.params["repository"]["full_name"],
|
||||
ref when is_binary(ref) <- conn.params["ref"],
|
||||
%{data: %{token: token, default_ref: default_ref}} <-
|
||||
repository_integration(conn.assigns[:project], repository) do
|
||||
assign(conn, :payload, %{
|
||||
default_ref: default_ref,
|
||||
ref: ref,
|
||||
repository: repository,
|
||||
token: token
|
||||
})
|
||||
else
|
||||
_ ->
|
||||
conn
|
||||
|> send_resp(:no_content, "")
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
||||
defp repository_integration(project, repository) do
|
||||
Integration
|
||||
|> IntegrationScope.from_project(project.id)
|
||||
|> IntegrationScope.from_service("github")
|
||||
|> IntegrationScope.from_data_repository(repository)
|
||||
|> first()
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
defp filter_event_type(conn, _) do
|
||||
conn
|
||||
|> get_req_header("x-github-event")
|
||||
|> case do
|
||||
["push"] ->
|
||||
conn
|
||||
|
||||
["ping"] ->
|
||||
conn
|
||||
|> send_resp(:ok, "pong")
|
||||
|> halt()
|
||||
|
||||
_ ->
|
||||
conn
|
||||
|> send_resp(:not_implemented, "")
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
end
|
@ -79,8 +79,6 @@ defmodule Accent.Router do
|
||||
# File export
|
||||
get("/export", ExportController, [])
|
||||
get("/jipt-export", ExportJIPTController, [])
|
||||
|
||||
post("/hooks/github", Hook.GitHubController, [], as: :hooks_github)
|
||||
end
|
||||
|
||||
scope "/", Accent do
|
||||
|
@ -55,12 +55,10 @@ defmodule AccentTest.GraphQL.Requests.ProjectIntegrations do
|
||||
|
||||
test "create integration successfully", %{user: user, project: project, create_mutation: mutation} do
|
||||
variables = %{
|
||||
"service" => "GITHUB",
|
||||
"service" => "SLACK",
|
||||
"projectId" => project.id,
|
||||
"data" => %{
|
||||
"repository" => "foo/bar",
|
||||
"token" => "1234",
|
||||
"defaultRef" => "master"
|
||||
"url" => "https://slack.com/hook?token=foo"
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,12 +77,10 @@ defmodule AccentTest.GraphQL.Requests.ProjectIntegrations do
|
||||
|
||||
test "create integration with errors", %{user: user, project: project, create_mutation: mutation} do
|
||||
variables = %{
|
||||
"service" => "GITHUB",
|
||||
"service" => "SLACK",
|
||||
"projectId" => project.id,
|
||||
"data" => %{
|
||||
"repository" => "",
|
||||
"token" => "",
|
||||
"defaultRef" => ""
|
||||
"url" => ""
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,10 +97,8 @@ defmodule AccentTest.GraphQL.Requests.ProjectIntegrations do
|
||||
|
||||
validation_messages = get_in(data, [:data, "createProjectIntegration", "messages"])
|
||||
|
||||
assert length(validation_messages) === 3
|
||||
assert length(validation_messages) === 1
|
||||
|
||||
assert %{"code" => "required", "field" => "data.defaultRef"} in validation_messages
|
||||
assert %{"code" => "required", "field" => "data.repository"} in validation_messages
|
||||
assert %{"code" => "required", "field" => "data.token"} in validation_messages
|
||||
assert %{"code" => "required", "field" => "data.url"} in validation_messages
|
||||
end
|
||||
end
|
||||
|
@ -50,39 +50,6 @@ defmodule AccentTest.GraphQL.Resolvers.Integration do
|
||||
assert get_in(Repo.all(Integration), [Access.all(), Access.key(:events)]) == [["sync"]]
|
||||
end
|
||||
|
||||
test "create github", %{project: project, user: user} do
|
||||
context = %{context: %{conn: %PlugConn{assigns: %{current_user: user}}}}
|
||||
|
||||
{:ok, integration} =
|
||||
Resolver.create(
|
||||
project,
|
||||
%{service: "github", data: %{repository: "root/test", default_ref: "master", token: "1234"}},
|
||||
context
|
||||
)
|
||||
|
||||
assert integration.service == "github"
|
||||
assert integration.data.repository == "root/test"
|
||||
assert integration.data.default_ref == "master"
|
||||
assert integration.data.token == "1234"
|
||||
|
||||
assert get_in(Repo.all(Integration), [Access.all(), Access.key(:service)]) == ["github"]
|
||||
end
|
||||
|
||||
test "create github error", %{project: project, user: user} do
|
||||
context = %{context: %{conn: %PlugConn{assigns: %{current_user: user}}}}
|
||||
|
||||
{:ok, integration} =
|
||||
Resolver.create(
|
||||
project,
|
||||
%{service: "github", data: %{repository: "", default_ref: "master", token: "1234"}},
|
||||
context
|
||||
)
|
||||
|
||||
assert integration.changes.data.errors == [repository: {"can't be blank", [validation: :required]}]
|
||||
|
||||
assert Repo.all(Integration) == []
|
||||
end
|
||||
|
||||
test "create slack error", %{project: project, user: user} do
|
||||
context = %{context: %{conn: %PlugConn{assigns: %{current_user: user}}}}
|
||||
|
||||
|
@ -1,610 +0,0 @@
|
||||
defmodule AccentTest.Hook.Inbounds.GitHub do
|
||||
@moduledoc false
|
||||
use Accent.RepoCase, async: true
|
||||
|
||||
import Ecto.Query
|
||||
import Mox
|
||||
|
||||
alias Accent.Document
|
||||
alias Accent.Hook.Inbounds.GitHub, as: Consumer
|
||||
alias Accent.Hook.Inbounds.GitHub.FileServerMock
|
||||
alias Accent.Integration
|
||||
alias Accent.Language
|
||||
alias Accent.Operation
|
||||
alias Accent.ProjectCreator
|
||||
alias Accent.Repo
|
||||
alias Accent.Revision
|
||||
alias Accent.Translation
|
||||
alias Accent.User
|
||||
alias Accent.Version
|
||||
alias Ecto.UUID
|
||||
|
||||
setup :verify_on_exit!
|
||||
|
||||
setup do
|
||||
user = Repo.insert!(%User{email: "test@test.com"})
|
||||
language = Repo.insert!(%Language{name: "English", slug: UUID.generate()})
|
||||
|
||||
{:ok, project} =
|
||||
ProjectCreator.create(params: %{main_color: "#f00", name: "My project", language_id: language.id}, user: user)
|
||||
|
||||
document = Repo.insert!(%Document{project_id: project.id, path: "admin", format: "json"})
|
||||
|
||||
[project: project, document: document, user: user]
|
||||
end
|
||||
|
||||
def gettext_file do
|
||||
Base.encode64(~S(
|
||||
msgid "key"
|
||||
msgstr "value"
|
||||
))
|
||||
end
|
||||
|
||||
def json_file do
|
||||
Base.encode64(~S(
|
||||
{
|
||||
"key": "value"
|
||||
}
|
||||
))
|
||||
end
|
||||
|
||||
test "sync default version on default_ref develop", %{project: project, user: user} do
|
||||
config =
|
||||
%{
|
||||
"files" => [
|
||||
%{
|
||||
"format" => "gettext",
|
||||
"language" => "fr",
|
||||
"source" => "priv/fr/**/*.po"
|
||||
}
|
||||
]
|
||||
}
|
||||
|> Jason.encode!()
|
||||
|> Base.encode64()
|
||||
|
||||
FileServerMock
|
||||
|> expect(:get_path, fn "accent/test-repo/contents/accent.json?ref=develop", [{"Authorization", "token 1234"}] ->
|
||||
{:ok, %{body: %{"content" => config}}}
|
||||
end)
|
||||
|> expect(:get_path, fn "accent/test-repo/git/trees/develop?recursive=1", [{"Authorization", "token 1234"}] ->
|
||||
{:ok,
|
||||
%{
|
||||
body: %{
|
||||
"tree" => [
|
||||
%{
|
||||
"path" => "accent.json",
|
||||
"type" => "blob",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/1"
|
||||
},
|
||||
%{
|
||||
"path" => "Dockerfile",
|
||||
"type" => "blob",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/2"
|
||||
},
|
||||
%{
|
||||
"path" => "priv/fr",
|
||||
"type" => "tree",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/3"
|
||||
},
|
||||
%{
|
||||
"path" => "priv/fr",
|
||||
"type" => "tree",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/4"
|
||||
},
|
||||
%{
|
||||
"path" => "priv/fr/admin.po",
|
||||
"type" => "blob",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/5"
|
||||
},
|
||||
%{
|
||||
"path" => "priv/en/admin.po",
|
||||
"type" => "blob",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/6"
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
end)
|
||||
|> expect(:get_path, fn "https://api.github.com/repos/accent/test-repo/git/blobs/5",
|
||||
[{"Authorization", "token 1234"}] ->
|
||||
{:ok, %{body: %{"content" => gettext_file()}}}
|
||||
end)
|
||||
|
||||
data = %{default_ref: "develop", repository: "accent/test-repo", token: "1234"}
|
||||
Repo.insert!(%Integration{project_id: project.id, user_id: user.id, service: "github", data: data})
|
||||
|
||||
context =
|
||||
to_worker_args(%Accent.Hook.Context{
|
||||
user_id: user.id,
|
||||
project_id: project.id,
|
||||
event: "push",
|
||||
payload: %{
|
||||
default_ref: data.default_ref,
|
||||
ref: "refs/heads/develop",
|
||||
repository: data.repository,
|
||||
token: data.token
|
||||
}
|
||||
})
|
||||
|
||||
Consumer.perform(%Oban.Job{args: context})
|
||||
|
||||
batch_operation =
|
||||
Operation
|
||||
|> where([o], o.batch == true)
|
||||
|> Repo.one()
|
||||
|
||||
operation =
|
||||
Operation
|
||||
|> where([o], o.batch == false)
|
||||
|> Repo.one()
|
||||
|
||||
translation =
|
||||
Translation
|
||||
|> where([t], t.key == ^"key")
|
||||
|> Repo.one()
|
||||
|
||||
assert batch_operation.action === "sync"
|
||||
assert operation.action === "new"
|
||||
assert operation.translation_id === translation.id
|
||||
assert translation.proposed_text === "value"
|
||||
end
|
||||
|
||||
test "sync with json file", %{project: project, user: user} do
|
||||
config =
|
||||
%{
|
||||
"files" => [
|
||||
%{
|
||||
"format" => "json",
|
||||
"language" => "fr",
|
||||
"source" => "priv/fr/**/*.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
|> Jason.encode!()
|
||||
|> Base.encode64()
|
||||
|
||||
FileServerMock
|
||||
|> expect(:get_path, fn "accent/test-repo/contents/accent.json?ref=develop", [{"Authorization", "token 1234"}] ->
|
||||
{:ok, %{body: %{"content" => config}}}
|
||||
end)
|
||||
|> expect(:get_path, fn "accent/test-repo/git/trees/develop?recursive=1", [{"Authorization", "token 1234"}] ->
|
||||
{:ok,
|
||||
%{
|
||||
body: %{
|
||||
"tree" => [
|
||||
%{
|
||||
"path" => "accent.json",
|
||||
"type" => "blob",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/1"
|
||||
},
|
||||
%{
|
||||
"path" => "Dockerfile",
|
||||
"type" => "blob",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/2"
|
||||
},
|
||||
%{
|
||||
"path" => "priv/fr",
|
||||
"type" => "tree",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/3"
|
||||
},
|
||||
%{
|
||||
"path" => "priv/fr",
|
||||
"type" => "tree",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/4"
|
||||
},
|
||||
%{
|
||||
"path" => "priv/fr/admin.json",
|
||||
"type" => "blob",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/5"
|
||||
},
|
||||
%{
|
||||
"path" => "priv/en/admin.json",
|
||||
"type" => "blob",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/6"
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
end)
|
||||
|> expect(:get_path, fn "https://api.github.com/repos/accent/test-repo/git/blobs/5",
|
||||
[{"Authorization", "token 1234"}] ->
|
||||
{:ok, %{body: %{"content" => json_file()}}}
|
||||
end)
|
||||
|
||||
data = %{default_ref: "develop", repository: "accent/test-repo", token: "1234"}
|
||||
Repo.insert!(%Integration{project_id: project.id, user_id: user.id, service: "github", data: data})
|
||||
|
||||
context =
|
||||
to_worker_args(%Accent.Hook.Context{
|
||||
user_id: user.id,
|
||||
project_id: project.id,
|
||||
event: "push",
|
||||
payload: %{
|
||||
default_ref: data.default_ref,
|
||||
ref: "refs/heads/develop",
|
||||
repository: data.repository,
|
||||
token: data.token
|
||||
}
|
||||
})
|
||||
|
||||
Consumer.perform(%Oban.Job{args: context})
|
||||
|
||||
batch_operation =
|
||||
Operation
|
||||
|> where([o], o.batch == true)
|
||||
|> Repo.one()
|
||||
|
||||
operation =
|
||||
Operation
|
||||
|> where([o], o.batch == false)
|
||||
|> Repo.one()
|
||||
|
||||
translation =
|
||||
Translation
|
||||
|> where([t], t.key == ^"key")
|
||||
|> Repo.one()
|
||||
|
||||
assert batch_operation.action === "sync"
|
||||
assert operation.action === "new"
|
||||
assert operation.translation_id === translation.id
|
||||
assert translation.proposed_text === "value"
|
||||
end
|
||||
|
||||
test "dont sync when default ref does not match", %{project: project, user: user} do
|
||||
data = %{default_ref: "master", repository: "accent/test-repo", token: "1234"}
|
||||
Repo.insert!(%Integration{project_id: project.id, user_id: user.id, service: "github", data: data})
|
||||
|
||||
context =
|
||||
to_worker_args(%Accent.Hook.Context{
|
||||
user_id: user.id,
|
||||
project_id: project.id,
|
||||
event: "push",
|
||||
payload: %{
|
||||
default_ref: data.default_ref,
|
||||
ref: "refs/heads/feature/my-feature",
|
||||
repository: data.repository,
|
||||
token: data.token
|
||||
}
|
||||
})
|
||||
|
||||
Consumer.perform(%Oban.Job{args: context})
|
||||
|
||||
translation =
|
||||
Translation
|
||||
|> where([t], t.key == ^"key")
|
||||
|> Repo.one()
|
||||
|
||||
refute translation
|
||||
end
|
||||
|
||||
test "sync tag version on matching ref tag", %{project: project, user: user} do
|
||||
config =
|
||||
%{
|
||||
"files" => [
|
||||
%{
|
||||
"format" => "gettext",
|
||||
"language" => "fr",
|
||||
"source" => "priv/fr/**/*.po"
|
||||
}
|
||||
]
|
||||
}
|
||||
|> Jason.encode!()
|
||||
|> Base.encode64()
|
||||
|
||||
FileServerMock
|
||||
|> expect(:get_path, fn "accent/test-repo/contents/accent.json?ref=v1.0.0", [{"Authorization", "token 1234"}] ->
|
||||
{:ok, %{body: %{"content" => config}}}
|
||||
end)
|
||||
|> expect(:get_path, fn "accent/test-repo/git/trees/v1.0.0?recursive=1", [{"Authorization", "token 1234"}] ->
|
||||
{:ok,
|
||||
%{
|
||||
body: %{
|
||||
"tree" => [
|
||||
%{
|
||||
"path" => "accent.json",
|
||||
"type" => "blob",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/1"
|
||||
},
|
||||
%{
|
||||
"path" => "Dockerfile",
|
||||
"type" => "blob",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/2"
|
||||
},
|
||||
%{
|
||||
"path" => "priv/fr",
|
||||
"type" => "tree",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/3"
|
||||
},
|
||||
%{
|
||||
"path" => "priv/fr",
|
||||
"type" => "tree",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/4"
|
||||
},
|
||||
%{
|
||||
"path" => "priv/fr/admin.po",
|
||||
"type" => "blob",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/5"
|
||||
},
|
||||
%{
|
||||
"path" => "priv/en/admin.po",
|
||||
"type" => "blob",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/6"
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
end)
|
||||
|> expect(:get_path, fn "https://api.github.com/repos/accent/test-repo/git/blobs/5",
|
||||
[{"Authorization", "token 1234"}] ->
|
||||
{:ok, %{body: %{"content" => gettext_file()}}}
|
||||
end)
|
||||
|
||||
version = Repo.insert!(%Version{project_id: project.id, user_id: user.id, tag: "v1.0.0", name: "First release"})
|
||||
data = %{default_ref: "master", repository: "accent/test-repo", token: "1234"}
|
||||
Repo.insert!(%Integration{project_id: project.id, user_id: user.id, service: "github", data: data})
|
||||
|
||||
context =
|
||||
to_worker_args(%Accent.Hook.Context{
|
||||
user_id: user.id,
|
||||
project_id: project.id,
|
||||
event: "push",
|
||||
payload: %{
|
||||
default_ref: data.default_ref,
|
||||
ref: "refs/tags/v1.0.0",
|
||||
repository: data.repository,
|
||||
token: data.token
|
||||
}
|
||||
})
|
||||
|
||||
Consumer.perform(%Oban.Job{args: context})
|
||||
|
||||
batch_operation =
|
||||
Operation
|
||||
|> where([o], o.batch == true and o.version_id == ^version.id)
|
||||
|> Repo.one()
|
||||
|
||||
operation =
|
||||
Operation
|
||||
|> where([o], o.batch == false and o.version_id == ^version.id)
|
||||
|> Repo.one()
|
||||
|
||||
translation =
|
||||
Translation
|
||||
|> where([t], t.key == ^"key" and t.version_id == ^version.id)
|
||||
|> Repo.one()
|
||||
|
||||
assert batch_operation.action === "sync"
|
||||
assert operation.action === "new"
|
||||
assert operation.translation_id === translation.id
|
||||
assert translation.proposed_text === "value"
|
||||
end
|
||||
|
||||
test "add translations default version on default_ref develop", %{project: project, document: document, user: user} do
|
||||
language_slug = UUID.generate()
|
||||
language = Repo.insert!(%Language{name: "Other french", slug: language_slug})
|
||||
revision = Repo.insert!(%Revision{project_id: project.id, master: false, language: language})
|
||||
|
||||
translation =
|
||||
Repo.insert!(%Translation{
|
||||
revision_id: revision.id,
|
||||
document_id: document.id,
|
||||
key: "key",
|
||||
proposed_text: "a",
|
||||
corrected_text: "a"
|
||||
})
|
||||
|
||||
config =
|
||||
%{
|
||||
"files" => [
|
||||
%{
|
||||
"format" => "gettext",
|
||||
"language" => "fr",
|
||||
"source" => "priv/fr/**/*.po",
|
||||
"target" => "priv/%slug%/**/%document_path%.po"
|
||||
}
|
||||
]
|
||||
}
|
||||
|> Jason.encode!()
|
||||
|> Base.encode64()
|
||||
|
||||
FileServerMock
|
||||
|> expect(:get_path, fn "accent/test-repo/contents/accent.json?ref=develop", [{"Authorization", "token 1234"}] ->
|
||||
{:ok, %{body: %{"content" => config}}}
|
||||
end)
|
||||
|> expect(:get_path, fn "accent/test-repo/git/trees/develop?recursive=1", [{"Authorization", "token 1234"}] ->
|
||||
{:ok,
|
||||
%{
|
||||
body: %{
|
||||
"tree" => [
|
||||
%{
|
||||
"path" => "accent.json",
|
||||
"type" => "blob",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/1"
|
||||
},
|
||||
%{
|
||||
"path" => "Dockerfile",
|
||||
"type" => "blob",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/2"
|
||||
},
|
||||
%{
|
||||
"path" => "priv/#{language_slug}",
|
||||
"type" => "tree",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/4"
|
||||
},
|
||||
%{
|
||||
"path" => "priv/#{language_slug}/admin.po",
|
||||
"type" => "blob",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/6"
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
end)
|
||||
|> expect(:get_path, fn "https://api.github.com/repos/accent/test-repo/git/blobs/6",
|
||||
[{"Authorization", "token 1234"}] ->
|
||||
{:ok, %{body: %{"content" => gettext_file()}}}
|
||||
end)
|
||||
|
||||
data = %{default_ref: "develop", repository: "accent/test-repo", token: "1234"}
|
||||
Repo.insert!(%Integration{project_id: project.id, user_id: user.id, service: "github", data: data})
|
||||
|
||||
context =
|
||||
to_worker_args(%Accent.Hook.Context{
|
||||
user_id: user.id,
|
||||
project_id: project.id,
|
||||
event: "push",
|
||||
payload: %{
|
||||
default_ref: data.default_ref,
|
||||
ref: "refs/heads/develop",
|
||||
repository: data.repository,
|
||||
token: data.token
|
||||
}
|
||||
})
|
||||
|
||||
Consumer.perform(%Oban.Job{args: context})
|
||||
|
||||
batch_operation =
|
||||
Operation
|
||||
|> where([o], o.batch == true)
|
||||
|> Repo.one()
|
||||
|
||||
operation =
|
||||
Operation
|
||||
|> where([o], o.batch == false)
|
||||
|> Repo.one()
|
||||
|
||||
updated_translation =
|
||||
Translation
|
||||
|> where([t], t.key == ^"key")
|
||||
|> Repo.one()
|
||||
|
||||
assert batch_operation.action === "merge"
|
||||
assert operation.action === "merge_on_proposed"
|
||||
assert operation.translation_id === translation.id
|
||||
|
||||
assert operation.previous_translation === %Accent.PreviousTranslation{
|
||||
corrected_text: "a",
|
||||
proposed_text: "a",
|
||||
value_type: "string"
|
||||
}
|
||||
|
||||
assert updated_translation.conflicted_text === "a"
|
||||
assert updated_translation.proposed_text === "value"
|
||||
end
|
||||
|
||||
test "add translations with language overrides", %{project: project, document: document, user: user} do
|
||||
language_override = UUID.generate()
|
||||
language_slug = UUID.generate()
|
||||
language = Repo.insert!(%Language{name: "Other french", slug: language_slug})
|
||||
|
||||
revision =
|
||||
Repo.insert!(%Revision{project_id: project.id, master: false, language: language, slug: language_override})
|
||||
|
||||
translation =
|
||||
Repo.insert!(%Translation{
|
||||
revision_id: revision.id,
|
||||
document_id: document.id,
|
||||
key: "key",
|
||||
proposed_text: "a",
|
||||
corrected_text: "a"
|
||||
})
|
||||
|
||||
config =
|
||||
%{
|
||||
"files" => [
|
||||
%{
|
||||
"format" => "gettext",
|
||||
"language" => "fr",
|
||||
"source" => "priv/fr/**/*.po",
|
||||
"target" => "priv/%slug%/**/%document_path%.po"
|
||||
}
|
||||
]
|
||||
}
|
||||
|> Jason.encode!()
|
||||
|> Base.encode64()
|
||||
|
||||
FileServerMock
|
||||
|> expect(:get_path, fn "accent/test-repo/contents/accent.json?ref=develop", [{"Authorization", "token 1234"}] ->
|
||||
{:ok, %{body: %{"content" => config}}}
|
||||
end)
|
||||
|> expect(:get_path, fn "accent/test-repo/git/trees/develop?recursive=1", [{"Authorization", "token 1234"}] ->
|
||||
{:ok,
|
||||
%{
|
||||
body: %{
|
||||
"tree" => [
|
||||
%{
|
||||
"path" => "accent.json",
|
||||
"type" => "blob",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/1"
|
||||
},
|
||||
%{
|
||||
"path" => "Dockerfile",
|
||||
"type" => "blob",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/2"
|
||||
},
|
||||
%{
|
||||
"path" => "priv/#{language_override}",
|
||||
"type" => "tree",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/4"
|
||||
},
|
||||
%{
|
||||
"path" => "priv/#{language_override}/admin.po",
|
||||
"type" => "blob",
|
||||
"url" => "https://api.github.com/repos/accent/test-repo/git/blobs/6"
|
||||
}
|
||||
]
|
||||
}
|
||||
}}
|
||||
end)
|
||||
|> expect(:get_path, fn "https://api.github.com/repos/accent/test-repo/git/blobs/6",
|
||||
[{"Authorization", "token 1234"}] ->
|
||||
{:ok, %{body: %{"content" => gettext_file()}}}
|
||||
end)
|
||||
|
||||
data = %{default_ref: "develop", repository: "accent/test-repo", token: "1234"}
|
||||
Repo.insert!(%Integration{project_id: project.id, user_id: user.id, service: "github", data: data})
|
||||
|
||||
context =
|
||||
to_worker_args(%Accent.Hook.Context{
|
||||
user_id: user.id,
|
||||
project_id: project.id,
|
||||
event: "push",
|
||||
payload: %{
|
||||
default_ref: data.default_ref,
|
||||
ref: "refs/heads/develop",
|
||||
repository: data.repository,
|
||||
token: data.token
|
||||
}
|
||||
})
|
||||
|
||||
Consumer.perform(%Oban.Job{args: context})
|
||||
|
||||
batch_operation =
|
||||
Operation
|
||||
|> where([o], o.batch == true)
|
||||
|> Repo.one()
|
||||
|
||||
operation =
|
||||
Operation
|
||||
|> where([o], o.batch == false)
|
||||
|> Repo.one()
|
||||
|
||||
updated_translation =
|
||||
Translation
|
||||
|> where([t], t.key == ^"key")
|
||||
|> Repo.one()
|
||||
|
||||
assert batch_operation.action === "merge"
|
||||
assert operation.action === "merge_on_proposed"
|
||||
assert operation.translation_id === translation.id
|
||||
|
||||
assert operation.previous_translation === %Accent.PreviousTranslation{
|
||||
corrected_text: "a",
|
||||
proposed_text: "a",
|
||||
value_type: "string"
|
||||
}
|
||||
|
||||
assert updated_translation.conflicted_text === "a"
|
||||
assert updated_translation.proposed_text === "value"
|
||||
end
|
||||
end
|
@ -1,15 +1,3 @@
|
||||
Mox.defmock(Accent.Hook.Inbounds.GitHub.FileServerMock, for: Accent.Hook.Inbounds.GitHub.FileServer)
|
||||
|
||||
defmodule Accent.Hook.Inbounds.Mock do
|
||||
@moduledoc false
|
||||
use Oban.Worker, queue: :hook
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(_job) do
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Accent.Hook.Outbounds.Mock do
|
||||
@moduledoc false
|
||||
use Oban.Worker, queue: :hook
|
||||
|
@ -1,130 +0,0 @@
|
||||
defmodule AccentTest.Hook.GitHubController do
|
||||
use Accent.ConnCase
|
||||
|
||||
alias Accent.AccessToken
|
||||
alias Accent.Collaborator
|
||||
alias Accent.Integration
|
||||
alias Accent.Project
|
||||
alias Accent.Repo
|
||||
alias Accent.User
|
||||
|
||||
@user %User{email: "test@test.com"}
|
||||
|
||||
setup do
|
||||
user = Repo.insert!(@user)
|
||||
access_token = Repo.insert!(%AccessToken{user_id: user.id, token: "test-token"})
|
||||
project = Repo.insert!(%Project{main_color: "#f00", name: "My project"})
|
||||
Repo.insert!(%Collaborator{project_id: project.id, user_id: user.id, role: "bot"})
|
||||
{:ok, [access_token: access_token, user: user, project: project]}
|
||||
end
|
||||
|
||||
test "acknowledge ping", %{access_token: access_token, conn: conn, project: project} do
|
||||
params = %{
|
||||
"ref" => "refs/heads/master",
|
||||
"repository" => %{
|
||||
"full_name" => "accent/test-repo"
|
||||
}
|
||||
}
|
||||
|
||||
response =
|
||||
conn
|
||||
|> put_req_header("x-github-event", "ping")
|
||||
|> post(hooks_github_path(conn, []) <> "?authorization=#{access_token.token}&project_id=#{project.id}", params)
|
||||
|
||||
assert response.status == 200
|
||||
assert response.resp_body == "pong"
|
||||
end
|
||||
|
||||
test "broadcast event on push", %{user: user, access_token: access_token, conn: conn, project: project} do
|
||||
params = %{
|
||||
"ref" => "refs/heads/master",
|
||||
"repository" => %{
|
||||
"full_name" => "accent/test-repo"
|
||||
}
|
||||
}
|
||||
|
||||
data = %{default_ref: "master", repository: "accent/test-repo", token: "1234"}
|
||||
Repo.insert!(%Integration{project_id: project.id, user_id: user.id, service: "github", data: data})
|
||||
|
||||
response =
|
||||
conn
|
||||
|> put_req_header("x-github-event", "push")
|
||||
|> post(hooks_github_path(conn, []) <> "?authorization=#{access_token.token}&project_id=#{project.id}", params)
|
||||
|
||||
assert response.status == 204
|
||||
|
||||
assert_enqueued(
|
||||
worker: Accent.Hook.Inbounds.Mock,
|
||||
args: %{
|
||||
"event" => "sync",
|
||||
"payload" => %{
|
||||
"default_ref" => "master",
|
||||
"ref" => "refs/heads/master",
|
||||
"repository" => "accent/test-repo",
|
||||
"token" => "1234"
|
||||
},
|
||||
"project_id" => project.id,
|
||||
"user_id" => user.id
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
test "don’t broadcast event on other event", %{user: user, access_token: access_token, conn: conn, project: project} do
|
||||
params = %{
|
||||
"ref" => "refs/heads/master",
|
||||
"repository" => %{
|
||||
"full_name" => "accent/test-repo"
|
||||
}
|
||||
}
|
||||
|
||||
data = %{default_ref: "master", repository: "accent/test-repo", token: "1234"}
|
||||
Repo.insert!(%Integration{project_id: project.id, user_id: user.id, service: "github", data: data})
|
||||
|
||||
response =
|
||||
conn
|
||||
|> put_req_header("x-github-event", "pull_request_comment")
|
||||
|> post(hooks_github_path(conn, []) <> "?authorization=#{access_token.token}&project_id=#{project.id}", params)
|
||||
|
||||
assert response.status == 501
|
||||
end
|
||||
|
||||
test "don’t broadcast event on non existing integration", %{access_token: access_token, conn: conn, project: project} do
|
||||
params = %{
|
||||
"ref" => "refs/heads/master",
|
||||
"repository" => %{
|
||||
"full_name" => "accent/test-repo"
|
||||
}
|
||||
}
|
||||
|
||||
response =
|
||||
conn
|
||||
|> put_req_header("x-github-event", "push")
|
||||
|> post(hooks_github_path(conn, []) <> "?authorization=#{access_token.token}&project_id=#{project.id}", params)
|
||||
|
||||
assert response.status == 204
|
||||
end
|
||||
|
||||
test "don’t broadcast event on non matching integration", %{
|
||||
user: user,
|
||||
access_token: access_token,
|
||||
conn: conn,
|
||||
project: project
|
||||
} do
|
||||
params = %{
|
||||
"ref" => "refs/heads/master",
|
||||
"repository" => %{
|
||||
"full_name" => "accent/test-repo"
|
||||
}
|
||||
}
|
||||
|
||||
data = %{default_ref: "master", repository: "accent/other-repo", token: "1234"}
|
||||
Repo.insert!(%Integration{project_id: project.id, user_id: user.id, service: "github", data: data})
|
||||
|
||||
response =
|
||||
conn
|
||||
|> put_req_header("x-github-event", "push")
|
||||
|> post(hooks_github_path(conn, []) <> "?authorization=#{access_token.token}&project_id=#{project.id}", params)
|
||||
|
||||
assert response.status == 204
|
||||
end
|
||||
end
|
@ -494,9 +494,6 @@
|
||||
"last_executed_at": "Last executed at:",
|
||||
"data": {
|
||||
"azure_storage_container_sas": "SAS URL",
|
||||
"default_ref": "Default ref",
|
||||
"repository": "Repository",
|
||||
"token": "Token",
|
||||
"url": "URL"
|
||||
},
|
||||
"token_how": "How?",
|
||||
@ -979,7 +976,6 @@
|
||||
},
|
||||
"integration_services": {
|
||||
"DISCORD": "Discord",
|
||||
"GITHUB": "GitHub",
|
||||
"SLACK": "Slack",
|
||||
"AZURE_STORAGE_CONTAINER": "Azure Storage Container"
|
||||
},
|
||||
|
@ -510,9 +510,6 @@
|
||||
"delete": "Supprimer",
|
||||
"data": {
|
||||
"azure_storage_container_sas": "SAS URL",
|
||||
"default_ref": "Réf par défaut",
|
||||
"repository": "Dépôt",
|
||||
"token": "Jeton",
|
||||
"url": "URL"
|
||||
},
|
||||
"token_how": "Comment?",
|
||||
@ -979,7 +976,6 @@
|
||||
},
|
||||
"integration_services": {
|
||||
"DISCORD": "Discord",
|
||||
"GITHUB": "GitHub",
|
||||
"SLACK": "Slack",
|
||||
"AZURE_STORAGE_CONTAINER": "Azure Storage Container"
|
||||
},
|
||||
|
@ -18,16 +18,13 @@ interface Args {
|
||||
service,
|
||||
events,
|
||||
integration,
|
||||
data: {url, repository, token, defaultRef, azureStorageContainerSas},
|
||||
data: {url, azureStorageContainerSas},
|
||||
}: {
|
||||
service: any;
|
||||
events: any;
|
||||
integration: any;
|
||||
data: {
|
||||
url: string;
|
||||
repository: string;
|
||||
token: string;
|
||||
defaultRef: string;
|
||||
azureStorageContainerSas: string;
|
||||
};
|
||||
}) => Promise<{errors: any}>;
|
||||
@ -58,22 +55,13 @@ export default class IntegrationsForm extends Component<Args> {
|
||||
@tracked
|
||||
events: string[];
|
||||
|
||||
@tracked
|
||||
repository: string;
|
||||
|
||||
@tracked
|
||||
token: string;
|
||||
|
||||
@tracked
|
||||
azureStorageContainerSas: string;
|
||||
|
||||
@tracked
|
||||
azureStorageContainerSasBaseUrl: string;
|
||||
|
||||
@tracked
|
||||
defaultRef = 'main';
|
||||
|
||||
services = ['AZURE_STORAGE_CONTAINER', 'SLACK', 'GITHUB', 'DISCORD'];
|
||||
services = ['AZURE_STORAGE_CONTAINER', 'SLACK', 'DISCORD'];
|
||||
|
||||
@not('url')
|
||||
emptyUrl: boolean;
|
||||
@ -112,8 +100,6 @@ export default class IntegrationsForm extends Component<Args> {
|
||||
events: [],
|
||||
data: {
|
||||
url: this.url,
|
||||
repository: this.repository,
|
||||
defaultRef: this.defaultRef,
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -121,8 +107,6 @@ export default class IntegrationsForm extends Component<Args> {
|
||||
this.service = this.integration.service || this.services[0];
|
||||
this.url = this.integration.data.url;
|
||||
this.events = this.integration.events;
|
||||
this.repository = this.integration.data.repository;
|
||||
this.defaultRef = this.integration.data.defaultRef;
|
||||
this.azureStorageContainerSasBaseUrl = this.integration.data.sasBaseUrl;
|
||||
}
|
||||
|
||||
@ -146,21 +130,6 @@ export default class IntegrationsForm extends Component<Args> {
|
||||
this.events = events;
|
||||
}
|
||||
|
||||
@action
|
||||
setRepository(repository: string) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@action
|
||||
setToken(token: string) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
@action
|
||||
setDefaultRef(defaultRef: string) {
|
||||
this.defaultRef = defaultRef;
|
||||
}
|
||||
|
||||
@action
|
||||
setAzureStorageContainerSas(sas: string) {
|
||||
this.azureStorageContainerSas = sas;
|
||||
@ -176,9 +145,6 @@ export default class IntegrationsForm extends Component<Args> {
|
||||
integration: this.integration.newRecord ? null : this.integration,
|
||||
data: {
|
||||
url: this.url,
|
||||
repository: this.repository,
|
||||
token: this.token,
|
||||
defaultRef: this.defaultRef,
|
||||
azureStorageContainerSas: this.azureStorageContainerSas,
|
||||
},
|
||||
});
|
||||
|
@ -2,18 +2,12 @@ import Component from '@glimmer/component';
|
||||
import {action} from '@ember/object';
|
||||
|
||||
interface Args {
|
||||
repository: any;
|
||||
defaultRef: any;
|
||||
token: any;
|
||||
errors: any;
|
||||
url: any;
|
||||
project: any;
|
||||
events: any;
|
||||
onChangeUrl: (url: string) => void;
|
||||
onChangeEventsChecked: (events: string[]) => void;
|
||||
onChangeRepository: (repository: string) => void;
|
||||
onChangeToken: (token: string) => void;
|
||||
onChangeDefaultRef: (defaultRef: string) => void;
|
||||
}
|
||||
|
||||
export default class Discord extends Component<Args> {
|
||||
|
@ -1,51 +0,0 @@
|
||||
import fmt from 'simple-fmt';
|
||||
import Component from '@glimmer/component';
|
||||
import {action} from '@ember/object';
|
||||
import config from 'accent-webapp/config/environment';
|
||||
|
||||
interface Args {
|
||||
repository: any;
|
||||
defaultRef: any;
|
||||
token: any;
|
||||
errors: any;
|
||||
url: any;
|
||||
project: any;
|
||||
onChangeUrl: (url: string) => void;
|
||||
onChangeRepository: (repository: string) => void;
|
||||
onChangeToken: (token: string) => void;
|
||||
onChangeDefaultRef: (defaultRef: string) => void;
|
||||
}
|
||||
|
||||
export default class GitHub extends Component<Args> {
|
||||
get webhookUrl() {
|
||||
const host = window.location.origin;
|
||||
|
||||
return `${host}${fmt(
|
||||
config.API.HOOKS_PATH,
|
||||
'github',
|
||||
this.args.project.id,
|
||||
'<YOUR_API_TOKEN_HERE>'
|
||||
)}`;
|
||||
}
|
||||
|
||||
@action
|
||||
changeRepository(event: Event) {
|
||||
const target = event.target as HTMLInputElement;
|
||||
|
||||
this.args.onChangeRepository(target.value);
|
||||
}
|
||||
|
||||
@action
|
||||
changeToken(event: Event) {
|
||||
const target = event.target as HTMLInputElement;
|
||||
|
||||
this.args.onChangeToken(target.value);
|
||||
}
|
||||
|
||||
@action
|
||||
changeDefaultRef(event: Event) {
|
||||
const target = event.target as HTMLInputElement;
|
||||
|
||||
this.args.onChangeDefaultRef(target.value);
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
.instructions {
|
||||
border-top: 1px solid var(--background-light-highlight);
|
||||
padding-top: 10px;
|
||||
margin: 20px 0 10px;
|
||||
}
|
||||
|
||||
.instructions-text {
|
||||
margin-top: 7px;
|
||||
font-style: italic;
|
||||
color: #555;
|
||||
|
||||
a {
|
||||
font-family: var(--font-monospace);
|
||||
color: var(--color-primary);
|
||||
text-decoration: none;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.data-control {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.data-title {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.data-title-help {
|
||||
margin-left: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: normal;
|
||||
text-decoration: none;
|
||||
color: var(--color-primary);
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.textInput {
|
||||
@extend %textInput;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 0;
|
||||
padding: 8px 10px;
|
||||
margin-right: 10px;
|
||||
background: #fafafa;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
font-family: var(--font-monospace);
|
||||
font-size: 11px;
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
<ProjectSettings::Integrations::Form::DataControlText
|
||||
@error={{field-error @errors 'data.repository'}}
|
||||
@label={{t 'components.project_settings.integrations.data.repository'}}
|
||||
@placeholder='mirego/accent'
|
||||
@value={{@repository}}
|
||||
@onChange={{this.changeRepository}}
|
||||
/>
|
||||
|
||||
<ProjectSettings::Integrations::Form::DataControlText
|
||||
@error={{field-error @errors 'data.token'}}
|
||||
@label={{t 'components.project_settings.integrations.data.token'}}
|
||||
@placeholder='aaaa-bbbb-cccc'
|
||||
@value={{@token}}
|
||||
@onChange={{this.changeToken}}
|
||||
@helpLinkHref='https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line'
|
||||
@helpLinkTitle={{t 'components.project_settings.integrations.token_how'}}
|
||||
/>
|
||||
|
||||
<ProjectSettings::Integrations::Form::DataControlText
|
||||
@error={{field-error @errors 'data.defaultRef'}}
|
||||
@label={{t 'components.project_settings.integrations.data.default_ref'}}
|
||||
@value={{@defaultRef}}
|
||||
@onChange={{this.changeDefaultRef}}
|
||||
/>
|
||||
|
||||
{{#if this.webhookUrl}}
|
||||
<div local-class='instructions'>
|
||||
<label local-class='data-title'>
|
||||
{{t 'components.project_settings.integrations.github_webhook_url'}}
|
||||
<a local-class='data-title-help' target='_blank' rel='noopener noreferrer' href='https://developer.github.com/webhooks/creating'>
|
||||
{{t 'components.project_settings.integrations.github_webhook_url_how'}}
|
||||
</a>
|
||||
</label>
|
||||
|
||||
<input readonly='' onClick='this.select();' local-class='textInput' value={{this.webhookUrl}} />
|
||||
|
||||
<div local-class='instructions-text'>
|
||||
<p>
|
||||
{{t 'components.project_settings.integrations.github_webhook_accent_cli_1' htmlSafe=true}}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{{t 'components.project_settings.integrations.github_webhook_accent_cli_2' htmlSafe=true}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
@ -2,18 +2,12 @@ import Component from '@glimmer/component';
|
||||
import {action} from '@ember/object';
|
||||
|
||||
interface Args {
|
||||
repository: any;
|
||||
defaultRef: any;
|
||||
token: any;
|
||||
errors: any;
|
||||
url: any;
|
||||
project: any;
|
||||
events: any;
|
||||
onChangeUrl: (url: string) => void;
|
||||
onChangeEventsChecked: (events: string[]) => void;
|
||||
onChangeRepository: (repository: string) => void;
|
||||
onChangeToken: (token: string) => void;
|
||||
onChangeDefaultRef: (defaultRef: string) => void;
|
||||
}
|
||||
|
||||
export default class Slack extends Component<Args> {
|
||||
|
@ -22,19 +22,13 @@
|
||||
<div local-class='data'>
|
||||
{{component
|
||||
this.dataFormComponent
|
||||
repository=this.repository
|
||||
defaultRef=this.defaultRef
|
||||
token=this.token
|
||||
errors=this.errors
|
||||
url=this.url
|
||||
project=@project
|
||||
events=this.events
|
||||
onChangeUrl=(fn this.setUrl)
|
||||
onChangeEventsChecked=(fn this.setEventsChecked)
|
||||
onChangeRepository=(fn this.setRepository)
|
||||
onChangeToken=(fn this.setToken)
|
||||
onChangeDefaultRef=(fn this.setDefaultRef)
|
||||
onChangeSas=(fn this.setAzureStorageContainerSas)
|
||||
onChangeEventsChecked=(fn this.setEventsChecked)
|
||||
}}
|
||||
</div>
|
||||
|
||||
@ -50,4 +44,4 @@
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
<span local-class='details-preview'>
|
||||
{{@integration.data.url}}
|
||||
{{@integration.data.repository}}
|
||||
{{@integration.data.sasBaseUrl}}
|
||||
|
||||
{{#if @integration.lastExecutedAt}}
|
||||
@ -53,4 +52,4 @@
|
||||
<AccModal @small={{true}} @onClose={{fn this.toggleExecuting}}>
|
||||
{{component this.dataExecuteComponent integration=@integration close=this.toggleExecuting}}
|
||||
</AccModal>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
@ -27,14 +27,6 @@ export default gql`
|
||||
}
|
||||
}
|
||||
|
||||
... on ProjectIntegrationGithub {
|
||||
data {
|
||||
id
|
||||
repository
|
||||
defaultRef
|
||||
}
|
||||
}
|
||||
|
||||
... on ProjectIntegrationAzureStorageContainer {
|
||||
lastExecutedAt
|
||||
data {
|
||||
|
@ -27,8 +27,7 @@ const fragmentMatcher = new IntrospectionFragmentMatcher({
|
||||
possibleTypes: [
|
||||
{name: 'ProjectIntegrationAzureStorageContainer'},
|
||||
{name: 'ProjectIntegrationDiscord'},
|
||||
{name: 'ProjectIntegrationSlack'},
|
||||
{name: 'ProjectIntegrationGithub'},
|
||||
{name: 'ProjectIntegrationSlack'}
|
||||
],
|
||||
},
|
||||
],
|
||||
|
Loading…
Reference in New Issue
Block a user