Skip to content

Commit ab13acc

Browse files
authored
feat: onboarding, org dashboard, exclusive bounties, provider account mgmt (#97)
1 parent f84e525 commit ab13acc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1991
-658
lines changed

lib/algora/accounts/accounts.ex

Lines changed: 96 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,15 @@ defmodule Algora.Accounts do
66
alias Algora.Accounts.Identity
77
alias Algora.Accounts.User
88
alias Algora.Bounties.Bounty
9+
alias Algora.Contracts.Contract
910
alias Algora.Github
1011
alias Algora.Organizations
1112
alias Algora.Organizations.Member
1213
alias Algora.Payments.Transaction
1314
alias Algora.Repo
15+
alias Algora.Workspace.Contributor
16+
alias Algora.Workspace.Installation
17+
alias Algora.Workspace.Repository
1418

1519
require Algora.SQL
1620

@@ -246,7 +250,7 @@ defmodule Algora.Accounts do
246250
@doc """
247251
Registers a user from their GitHub information.
248252
"""
249-
def register_github_user(primary_email, info, emails, token) do
253+
def register_github_user(current_user, primary_email, info, emails, token) do
250254
query =
251255
from(u in User,
252256
left_join: i in Identity,
@@ -257,10 +261,16 @@ defmodule Algora.Accounts do
257261
select: {u, i}
258262
)
259263

260-
case Repo.one(query) do
264+
account =
265+
case Repo.all(query) do
266+
[] -> nil
267+
[{user, identity}] -> {user, identity}
268+
records -> Enum.find(records, fn {user, _} -> user.id == current_user.id end)
269+
end
270+
271+
case account do
261272
nil -> create_user(info, primary_email, emails, token)
262-
{user, nil} -> update_user(user, info, primary_email, emails, token)
263-
{user, _identity} -> update_github_token(user, token)
273+
{user, identity} -> update_user(user, identity, info, primary_email, emails, token)
264274
end
265275
end
266276

@@ -274,15 +284,74 @@ defmodule Algora.Accounts do
274284
|> Repo.insert()
275285
end
276286

277-
def update_user(user, info, primary_email, emails, token) do
278-
with {:ok, _} <-
279-
user
280-
|> Identity.github_registration_changeset(info, primary_email, emails, token)
281-
|> Repo.insert() do
282-
user
283-
|> User.github_registration_changeset(info, primary_email, emails, token)
284-
|> Repo.update()
285-
end
287+
def update_user(user, identity, info, primary_email, emails, token) do
288+
old_user = Repo.get_by(User, provider: "github", provider_id: to_string(info["id"]))
289+
290+
identity_changeset = Identity.github_registration_changeset(user, info, primary_email, emails, token)
291+
292+
user_changeset = User.github_registration_changeset(user, info, primary_email, emails, token)
293+
294+
Repo.transact(fn ->
295+
delete_result =
296+
if identity do
297+
Repo.delete(identity)
298+
else
299+
{:ok, nil}
300+
end
301+
302+
migrate_result =
303+
if old_user && old_user.id != user.id do
304+
{:ok, old_user} =
305+
old_user
306+
|> change(provider: nil, provider_id: nil, provider_login: nil, provider_meta: nil)
307+
|> Repo.update()
308+
309+
# TODO: enqueue job
310+
migrate_user(old_user, user)
311+
312+
{:ok, old_user}
313+
else
314+
{:ok, nil}
315+
end
316+
317+
with {:ok, _} <- delete_result,
318+
{:ok, _} <- migrate_result,
319+
{:ok, _} <- Repo.insert(identity_changeset) do
320+
Repo.update(user_changeset)
321+
end
322+
end)
323+
end
324+
325+
def migrate_user(old_user, new_user) do
326+
Repo.update_all(
327+
from(r in Repository, where: r.user_id == ^old_user.id),
328+
set: [user_id: new_user.id]
329+
)
330+
331+
Repo.update_all(
332+
from(c in Contributor, where: c.user_id == ^old_user.id),
333+
set: [user_id: new_user.id]
334+
)
335+
336+
Repo.update_all(
337+
from(c in Contract, where: c.contractor_id == ^old_user.id),
338+
set: [contractor_id: new_user.id]
339+
)
340+
341+
Repo.update_all(
342+
from(i in Installation, where: i.owner_id == ^old_user.id),
343+
set: [owner_id: new_user.id]
344+
)
345+
346+
Repo.update_all(
347+
from(i in Installation, where: i.provider_user_id == ^old_user.id),
348+
set: [provider_user_id: new_user.id]
349+
)
350+
351+
Repo.update_all(
352+
from(i in Installation, where: i.connected_user_id == ^old_user.id),
353+
set: [connected_user_id: new_user.id]
354+
)
286355
end
287356

288357
# def get_user_by_provider_email(provider, email) when provider in [:github] do
@@ -352,19 +421,6 @@ defmodule Algora.Accounts do
352421
end
353422
end
354423

355-
defp update_github_token(%User{} = user, new_token) do
356-
identity =
357-
Repo.one!(from(i in Identity, where: i.user_id == ^user.id and i.provider == "github"))
358-
359-
{:ok, _} =
360-
identity
361-
|> change()
362-
|> put_change(:provider_token, new_token)
363-
|> Repo.update()
364-
365-
{:ok, Repo.preload(user, :identities, force: true)}
366-
end
367-
368424
def last_context(nil), do: "nil"
369425

370426
def last_context(%User{last_context: nil} = user) do
@@ -413,8 +469,20 @@ defmodule Algora.Accounts do
413469

414470
def get_last_context_user(%User{} = user) do
415471
case last_context(user) do
416-
"personal" -> user
417-
last_context -> get_user_by_handle(last_context)
472+
"personal" ->
473+
user
474+
475+
"preview/" <> ctx ->
476+
case String.split(ctx, "/") do
477+
[id, _repo_owner, _repo_name] -> get_user(id)
478+
_ -> nil
479+
end
480+
481+
"repo/" <> _repo_full_name ->
482+
user
483+
484+
last_context ->
485+
get_user_by_handle(last_context)
418486
end
419487
end
420488

lib/algora/accounts/schemas/user.ex

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ defmodule Algora.Accounts.User do
129129
params = %{
130130
"handle" => info["login"],
131131
"email" => primary_email,
132-
"display_name" => get_change(identity_changeset, :provider_name),
132+
"display_name" => info["name"],
133133
"bio" => info["bio"],
134134
"location" => info["location"],
135135
"avatar_url" => info["avatar_url"],
@@ -176,21 +176,33 @@ defmodule Algora.Accounts.User do
176176
Identity.github_registration_changeset(user, info, primary_email, emails, token)
177177

178178
if identity_changeset.valid? do
179-
params = %{
180-
"display_name" => user.display_name || get_change(identity_changeset, :provider_name),
181-
"bio" => user.bio || info["bio"],
182-
"location" => user.location || info["location"],
183-
"avatar_url" => user.avatar_url || info["avatar_url"],
184-
"website_url" => user.website_url || info["blog"],
185-
"github_url" => user.github_url || info["html_url"],
186-
"provider" => "github",
187-
"provider_id" => to_string(info["id"]),
188-
"provider_login" => info["login"],
189-
"provider_meta" => info
190-
}
179+
params =
180+
%{
181+
"handle" => user.handle || Algora.Organizations.ensure_unique_handle(info["login"]),
182+
"email" => user.email || primary_email,
183+
"display_name" => user.display_name || info["name"],
184+
"bio" => user.bio || info["bio"],
185+
"location" => user.location || info["location"],
186+
"avatar_url" => user.avatar_url || info["avatar_url"],
187+
"website_url" => user.website_url || info["blog"],
188+
"github_url" => user.github_url || info["html_url"],
189+
"provider" => "github",
190+
"provider_id" => to_string(info["id"]),
191+
"provider_login" => info["login"],
192+
"provider_meta" => info
193+
}
194+
195+
params =
196+
if is_nil(user.provider_id) do
197+
Map.put(params, "display_name", info["name"])
198+
else
199+
params
200+
end
191201

192202
user
193203
|> cast(params, [
204+
:handle,
205+
:email,
194206
:display_name,
195207
:bio,
196208
:location,
@@ -203,8 +215,11 @@ defmodule Algora.Accounts.User do
203215
:provider_meta
204216
])
205217
|> generate_id()
206-
|> validate_required([:display_name])
218+
|> validate_required([:email, :display_name, :handle])
219+
|> validate_handle()
207220
|> validate_email()
221+
|> unique_constraint(:email)
222+
|> unique_constraint(:handle)
208223
else
209224
user
210225
|> change()
@@ -270,7 +285,7 @@ defmodule Algora.Accounts.User do
270285

271286
def validate_handle(changeset) do
272287
reserved_words =
273-
~w(personal org admin support help security team staff official auth tip home dashboard bounties community user payment claims orgs projects jobs leaderboard onboarding pricing developers companies contracts community blog docs open hiring sdk api)
288+
~w(personal org admin support help security team staff official auth tip home dashboard bounties community user payment claims orgs projects jobs leaderboard onboarding pricing developers companies contracts community blog docs open hiring sdk api repo go preview)
274289

275290
changeset
276291
|> validate_format(:handle, ~r/^[a-zA-Z0-9_-]{2,32}$/)
@@ -320,8 +335,9 @@ defmodule Algora.Accounts.User do
320335
validate_inclusion(changeset, :timezone, Tzdata.zone_list())
321336
end
322337

323-
defp type_from_provider(:github, "Organization"), do: :organization
324-
defp type_from_provider(:github, _), do: :individual
338+
def type_from_provider(:github, "Bot"), do: :bot
339+
def type_from_provider(:github, "Organization"), do: :organization
340+
def type_from_provider(:github, _), do: :individual
325341

326342
def handle(%{handle: handle}) when is_binary(handle), do: handle
327343
def handle(%{provider_login: handle}), do: handle

lib/algora/admin/admin.ex

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@ defmodule Algora.Admin do
1616

1717
require Logger
1818

19+
def alert(message) do
20+
%{
21+
title: "Alert: #{message}",
22+
body: message,
23+
name: "Algora Alert",
24+
25+
}
26+
|> Algora.Activities.SendEmail.changeset()
27+
|> Repo.insert()
28+
end
29+
1930
def token!, do: System.fetch_env!("ADMIN_GITHUB_TOKEN")
2031

2132
def run(worker) do

0 commit comments

Comments
 (0)