Skip to content

Commit

Permalink
Merge pull request #1190 from code-corps/restrict-github-repo-to-sing…
Browse files Browse the repository at this point in the history
…le-project

Restrict GithubRepo to connect to single project
  • Loading branch information
joshsmith committed Nov 14, 2017
2 parents 302e89a + a7d1d3d commit 4764879
Show file tree
Hide file tree
Showing 34 changed files with 485 additions and 576 deletions.
1 change: 0 additions & 1 deletion config/prod.exs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ config :code_corps, :stripe_env, :prod
config :sentry,
environment_name: Mix.env || :prod

# TODO: Replace with actual adapter
config :code_corps, CodeCorps.Mailer,
adapter: Bamboo.PostmarkAdapter,
api_key: System.get_env("POSTMARK_API_KEY")
Expand Down
42 changes: 19 additions & 23 deletions lib/code_corps/github/adapters/comment.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,19 @@ defmodule CodeCorps.GitHub.Adapters.Comment do
GitHub.Adapters.Utils.BodyDecorator
}

@mapping [
@github_comment_to_comment_mapping [
{:created_at, [:github_created_at]},
{:markdown, [:body]},
{:modified_at, [:github_updated_at]}
]

@github_payload_to_comment_mapping [
{:created_at, ["created_at"]},
{:markdown, ["body"]},
{:modified_at, ["updated_at"]}
]

@doc ~S"""
Converts a Github Issue Comment payload into a set of attributes suitable for
creating or updating a `CodeCorps.Comment`
"""
@spec to_comment(map) :: map
def to_comment(%{} = payload) do
payload
|> BodyDecorator.remove_code_corps_header()
|> MapTransformer.transform(@mapping)
end

@github_comment_mapping [
@github_payload_to_github_comment_mapping [
{:body, ["body"]},
{:github_created_at, ["created_at"]},
{:github_id, ["id"]},
Expand All @@ -38,14 +33,15 @@ defmodule CodeCorps.GitHub.Adapters.Comment do
]

@doc ~S"""
Converts a `GithubComment` record into attributes with the same keys as the
GitHub API's Issue Comment
Converts a `CodeCorps.GithubComment` into a set of attributes suitable for
creating or updating a `CodeCorps.Comment`
"""
@spec to_comment_attrs(GithubComment.t) :: map
def to_comment_attrs(%GithubComment{} = github_comment) do
@spec to_comment(GithubComment.t) :: map
def to_comment(%GithubComment{} = github_comment) do
github_comment
|> Map.from_struct
|> MapTransformer.transform_inverse(@github_comment_mapping)
|> Map.from_struct()
|> BodyDecorator.remove_code_corps_header()
|> MapTransformer.transform(@github_comment_to_comment_mapping)
end

@doc ~S"""
Expand All @@ -54,20 +50,20 @@ defmodule CodeCorps.GitHub.Adapters.Comment do
"""
@spec to_github_comment(map) :: map
def to_github_comment(%{} = payload) do
payload |> MapTransformer.transform(@github_comment_mapping)
payload |> MapTransformer.transform(@github_payload_to_github_comment_mapping)
end

@autogenerated_github_keys ~w(created_at id updated_at)

@doc ~S"""
Converts a `CodeCorps.Comment` into a set of attributes suitable to create or
update an GitHub Issue Comment through the GitHub API.
Converts a `CodeCorps.Comment` into a set of attributes suitable for creating
or updating an GitHub Issue Comment through the GitHub API.
"""
@spec to_api(Comment.t) :: map
def to_api(%Comment{} = comment) do
comment
|> Map.from_struct
|> MapTransformer.transform_inverse(@mapping)
|> MapTransformer.transform_inverse(@github_payload_to_comment_mapping)
|> Map.drop(@autogenerated_github_keys)
|> BodyDecorator.add_code_corps_header(comment)
end
Expand Down
18 changes: 11 additions & 7 deletions lib/code_corps/github/adapters/utils/body_decorator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,16 @@ defmodule CodeCorps.GitHub.Adapters.Utils.BodyDecorator do
end

@spec remove_code_corps_header(map) :: map
def remove_code_corps_header(%{"body" => body} = attrs) when is_binary(body) do
clean_body = case body |> String.split(@separator) do
["Posted by" <> _rest | tail] -> tail |> Enum.join |> String.trim_leading
_ -> body
end
attrs |> Map.put("body", clean_body)
def remove_code_corps_header(%{body: _} = attrs) do
attrs |> Map.update(:body, nil, &clean_body/1)
end
def remove_code_corps_header(%{} = attrs), do: attrs

@spec clean_body(String.t | nil) :: String.t | nil
defp clean_body("Posted by " <> @separator <> _rest = body) do
body
|> String.split(@separator)
|> Enum.drop(1) |> Enum.join
|> String.trim_leading
end
defp clean_body(body), do: body
end
56 changes: 23 additions & 33 deletions lib/code_corps/github/sync/comment/comment.ex
Original file line number Diff line number Diff line change
@@ -1,49 +1,39 @@
defmodule CodeCorps.GitHub.Sync.Comment do
alias CodeCorps.{
GitHub,
GithubComment,
GithubIssue
}
alias GitHub.Sync.Comment.Comment, as: CommentCommentSyncer
alias GitHub.Sync.Comment.GithubComment, as: CommentGithubCommentSyncer
alias GitHub.Sync.User.RecordLinker, as: UserRecordLinker
alias CodeCorps.GitHub.Sync
alias Ecto.Multi

@doc ~S"""
Syncs a GitHub comment API payload with our data.
Creates an `Ecto.Multi` intended to process a GitHub issue comment related API
payload.
Expects a `CodeCorps.GithubIssue` record and a list of `CodeCorps.Task`
records passed in with the changes.
Expects a partial transaction outcome with `:github_issue` and :task keys.
The process is as follows:
Returns an `Ecto.Multi` with the follwing steps
- create a `CodeCorps.GithubComment` related to the `CodeCorps.GithubIssue`
- match the comment payload with a `CodeCorps.User` using
`CodeCorps.GitHub.Sync.User.RecordLinker`
- for each `CodeCorps.Task`:
- create or update `CodeCorps.Comment` for the `CodeCorps.Task`
- create or update a `CodeCorps.GithubComment` from the
provided `CodeCorps.GithubIssue` and API payload
- match the `CodeCorps.GithubComment` with a new or existing `CodeCorps.User`
- create or update a `CodeCorps.Comment` using the created
`CodeCorps.GithubComment`, related to the matched `CodeCorps.User` and the
provided `CodeCorps.Task`
"""
@spec sync(map, map) :: Multi.t
def sync(%{github_issue: github_issue, tasks: tasks}, payload) do
def sync(%{github_issue: github_issue, task: task}, %{} = payload) do
Multi.new
|> Multi.run(:github_comment, fn _ -> sync_github_comment(github_issue, payload) end)
|> Multi.run(:comment_user, fn %{github_comment: github_comment} -> UserRecordLinker.link_to(github_comment, payload) end)
|> Multi.run(:comments, fn %{github_comment: github_comment, comment_user: user} -> CommentCommentSyncer.sync_all(tasks, github_comment, user, payload) end)
|> Multi.run(:github_comment, fn _ -> Sync.Comment.GithubComment.create_or_update_comment(github_issue, payload) end)
|> Multi.run(:comment_user, fn %{github_comment: github_comment} -> Sync.User.RecordLinker.link_to(github_comment, payload) end)
|> Multi.run(:comment, fn %{github_comment: github_comment, comment_user: user} -> Sync.Comment.Comment.sync(task, github_comment, user) end)
end

@doc """
When provided a GitHub API payload, it deletes each `Comment` associated to
the specified `IssueComment` and then deletes the `GithubComment`.
@doc ~S"""
Creates an `Ecto.Multi` intended to delete a `CodeCorps.GithubComment`
specified by `github_id`, as well as 0 to 1 `CodeCorps.Comment` records
associated to `CodeCorps.GithubComment`
"""
@spec delete(map, map) :: Multi.t
def delete(_, %{"id" => github_id}) do
@spec delete(map) :: Multi.t
def delete(%{"id" => github_id}) do
Multi.new
|> Multi.run(:deleted_comments, fn _ -> CommentCommentSyncer.delete_all(github_id) end)
|> Multi.run(:deleted_github_comment, fn _ -> CommentGithubCommentSyncer.delete(github_id) end)
end

@spec sync_github_comment(GithubIssue.t, map) :: {:ok, GithubComment.t} | {:error, Ecto.Changeset.t}
defp sync_github_comment(github_issue, attrs) do
CommentGithubCommentSyncer.create_or_update_comment(github_issue, attrs)
|> Multi.run(:deleted_comments, fn _ -> Sync.Comment.Comment.delete(github_id) end)
|> Multi.run(:deleted_github_comment, fn _ -> Sync.Comment.GithubComment.delete(github_id) end)
end
end
55 changes: 13 additions & 42 deletions lib/code_corps/github/sync/comment/comment/changeset.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,59 +7,30 @@ defmodule CodeCorps.GitHub.Sync.Comment.Comment.Changeset do
alias CodeCorps.{
Comment,
GithubComment,
GitHub.Adapters,
Services.MarkdownRendererService,
Task,
User,
Validators.TimeValidator
}
alias CodeCorps.GitHub.Adapters.Comment, as: CommentAdapter
alias Ecto.Changeset

@create_attrs ~w(created_at markdown modified_at)a
@update_attrs ~w(markdown modified_at)a

@doc ~S"""
Constructs a changeset for syncing a task from a GitHub API Comment payload.
"""
@spec build_changeset(Comment.t, map, GithubComment.t, Task.t, User.t) :: Changeset.t
def build_changeset(
%Comment{id: nil} = comment,
%{} = attrs,
%GithubComment{} = github_comment,
%Task{} = task,
%User{} = user) do
comment |> create_changeset(attrs, github_comment, task, user)
end
def build_changeset(%Comment{} = comment, attrs, %GithubComment{}, %Task{}, %User{}) do
comment |> update_changeset(attrs)
end

@doc ~S"""
Constructs a changeset for syncing a task from a `GithubComment` record.
The function detects if the `CodeCorps.Comment` is to be inserted or updated
and acts accordingly.
"""
@spec build_changeset(Comment.t, GithubComment.t, Task.t, User.t) :: Changeset.t
def build_changeset(
%Comment{id: comment_id} = comment,
%GithubComment{} = github_comment,
%Task{} = task,
%User{} = user) do

comment_attrs = github_comment |> CommentAdapter.to_comment_attrs()
case is_nil(comment_id) do
true -> create_changeset(comment, comment_attrs, github_comment, task, user)
false -> update_changeset(comment, comment_attrs)
end
end

@create_attrs ~w(created_at markdown modified_at)a
@spec create_changeset(Comment.t, map, GithubComment.t, Task.t, User.t) :: Changeset.t
defp create_changeset(
%Comment{} = comment,
%{} = attrs,
%GithubComment{} = github_comment,
%Task{} = task,
%User{} = user) do
%Comment{id: nil} = comment,
%GithubComment{} = github_comment, %Task{} = task, %User{} = user) do

comment
|> Changeset.cast(CommentAdapter.to_comment(attrs), @create_attrs)
|> Changeset.cast(github_comment |> Adapters.Comment.to_comment, @create_attrs)
|> MarkdownRendererService.render_markdown_to_html(:markdown, :body)
|> Changeset.put_change(:created_from, "github")
|> Changeset.put_change(:modified_from, "github")
Expand All @@ -68,12 +39,12 @@ defmodule CodeCorps.GitHub.Sync.Comment.Comment.Changeset do
|> Changeset.put_change(:user, user)
|> Changeset.validate_required([:markdown, :body])
end
def build_changeset(
%Comment{} = comment,
%GithubComment{} = github_comment, %Task{}, %User{}) do

@update_attrs ~w(markdown modified_at)a
@spec update_changeset(Comment.t, map) :: Changeset.t
defp update_changeset(%Comment{} = comment, %{} = attrs) do
comment
|> Changeset.cast(CommentAdapter.to_comment(attrs), @update_attrs)
|> Changeset.cast(github_comment |> Adapters.Comment.to_comment, @update_attrs)
|> MarkdownRendererService.render_markdown_to_html(:markdown, :body)
|> Changeset.put_change(:modified_from, "github")
|> TimeValidator.validate_time_not_before(:modified_at)
Expand Down
Loading

0 comments on commit 4764879

Please sign in to comment.