Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update user profile #4488

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion lib/sanbase/accounts/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ defmodule Sanbase.Accounts.User do
field(:is_superuser, :boolean, default: false)
field(:twitter_id, :string)

field(:description, :string)
field(:website_link, :string)
field(:twitter_link, :string)

# GDPR related fields
field(:privacy_policy_accepted, :boolean, default: false)
field(:marketing_accepted, :boolean, default: false)
Expand Down Expand Up @@ -168,7 +172,10 @@ defmodule Sanbase.Accounts.User do
:twitter_id,
:username,
:name,
:registration_state
:registration_state,
:description,
:website_link,
:twitter_link
])
|> normalize_user_identificator(:username, attrs[:username])
|> normalize_user_identificator(:email, attrs[:email])
Expand All @@ -177,6 +184,8 @@ defmodule Sanbase.Accounts.User do
|> validate_change(:username, &validate_username_change/2)
|> validate_change(:email_candidate, &validate_email_candidate_change/2)
|> validate_change(:avatar_url, &validate_url_change/2)
|> validate_change(:website_link, &validate_url_simple/2)
|> validate_change(:twitter_link, &validate_url_simple/2)
|> unique_constraint(:email)
|> unique_constraint(:username)
|> unique_constraint(:stripe_customer_id)
Expand Down Expand Up @@ -395,4 +404,12 @@ defmodule Sanbase.Accounts.User do
)
|> Repo.all()
end

def update_profile(%__MODULE__{} = user, attrs) do
user
|> changeset(attrs)
|> Repo.update()

# |> emit_event(:update_profile, %{})
end
end
11 changes: 9 additions & 2 deletions lib/sanbase/accounts/user/user_validation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,17 @@ defmodule Sanbase.Accounts.User.Validation do
end
end

def validate_url_change(:avatar_url, url) do
def validate_url_change(field, url) do
case Sanbase.Validation.valid_url?(url) do
:ok -> []
{:error, msg} -> [avatar_url: msg]
{:error, msg} -> [{field, msg}]
end
end

def validate_url_simple(field, url) do
case Sanbase.Validation.valid_url_simple?(url) do
true -> []
false -> [{field, "Invalid URL"}]
end
end
end
5 changes: 5 additions & 0 deletions lib/sanbase/utils/validation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ defmodule Sanbase.Validation do
end
end

def valid_url_simple?(url) do
uri = URI.parse(url)
uri.scheme != nil and uri.host != nil
end

# Private functions

defp time_window_format_check(time_window) do
Expand Down
13 changes: 13 additions & 0 deletions lib/sanbase_web/graphql/resolvers/user/user_resolver.ex
Original file line number Diff line number Diff line change
Expand Up @@ -319,4 +319,17 @@ defmodule SanbaseWeb.Graphql.Resolvers.UserResolver do
{:ok, Dataloader.get(loader, SanbaseDataloader, :users_by_id, user_id)}
end)
end

def update_profile(_root, args, %{context: %{auth: %{current_user: user}}}) do
case User.update_profile(user, args) do
{:ok, user} ->
{:ok, user}

{:error, changeset} ->
{
:error,
message: "Cannot update user profile", details: changeset_errors(changeset)
}
end
end
end
9 changes: 9 additions & 0 deletions lib/sanbase_web/graphql/schema/queries/user_queries.ex
Original file line number Diff line number Diff line change
Expand Up @@ -208,5 +208,14 @@ defmodule SanbaseWeb.Graphql.Schema.UserQueries do
middleware(UserAuth)
resolve(&UserResolver.self_reset_api_rate_limits/3)
end

field :update_user_profile, :user do
arg(:description, :string)
arg(:website_link, :string)
arg(:twitter_link, :string)

middleware(JWTAuth)
resolve(&UserResolver.update_profile/3)
end
end
end
6 changes: 6 additions & 0 deletions lib/sanbase_web/graphql/schema/types/user_types.ex
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ defmodule SanbaseWeb.Graphql.UserTypes do
field(:name, :string)
field(:username, :string)
field(:avatar_url, :string)
field(:description, :string)
field(:website_link, :string)
field(:twitter_link, :string)

field :triggers, list_of(:trigger) do
cache_resolve(&UserTriggerResolver.public_triggers/3, ttl: 60)
Expand Down Expand Up @@ -129,6 +132,9 @@ defmodule SanbaseWeb.Graphql.UserTypes do
field(:stripe_customer_id, :string)
field(:inserted_at, non_null(:datetime))
field(:updated_at, non_null(:datetime))
field(:description, :string)
field(:website_link, :string)
field(:twitter_link, :string)

field :queries_executions_info, :queries_executions_info do
resolve(&UserResolver.queries_executions_info/3)
Expand Down
19 changes: 19 additions & 0 deletions priv/repo/migrations/20241126110304_add_user_profile_fields.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
defmodule Sanbase.Repo.Migrations.AddUserProfileFields do
use Ecto.Migration

def change do
alter table(:users) do
# Text field for user's description/bio
add(:description, :text)

# Boolean flag for Santiment team membership
add(:is_santiment_team, :boolean, default: false)
Comment on lines +9 to +10
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should handle this with roles.


# Social links
add(:twitter_link, :string)
add(:website_link, :string)
end

create(index(:users, [:is_santiment_team]))
end
end
19 changes: 16 additions & 3 deletions priv/repo/structure.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
-- PostgreSQL database dump
--

-- Dumped from database version 14.12 (Homebrew)
-- Dumped by pg_dump version 14.12 (Homebrew)
-- Dumped from database version 15.1 (Homebrew)
-- Dumped by pg_dump version 15.1 (Homebrew)

SET statement_timeout = 0;
SET lock_timeout = 0;
Expand Down Expand Up @@ -4559,7 +4559,11 @@ CREATE TABLE public.users (
is_superuser boolean DEFAULT false,
twitter_id character varying(255) DEFAULT NULL::character varying,
name character varying(255),
registration_state jsonb DEFAULT '{"state": "init"}'::jsonb
registration_state jsonb DEFAULT '{"state": "init"}'::jsonb,
description text,
is_santiment_team boolean DEFAULT false,
twitter_link character varying(255),
website_link character varying(255)
);


Expand Down Expand Up @@ -7806,6 +7810,13 @@ CREATE UNIQUE INDEX users_email_index ON public.users USING btree (email);
CREATE UNIQUE INDEX users_email_token_index ON public.users USING btree (email_token);


--
-- Name: users_is_santiment_team_index; Type: INDEX; Schema: public; Owner: -
--

CREATE INDEX users_is_santiment_team_index ON public.users USING btree (is_santiment_team);


--
-- Name: users_stripe_customer_id_index; Type: INDEX; Schema: public; Owner: -
--
Expand Down Expand Up @@ -9674,9 +9685,11 @@ INSERT INTO public."schema_migrations" (version) VALUES (20241029080754);
INSERT INTO public."schema_migrations" (version) VALUES (20241029082533);
INSERT INTO public."schema_migrations" (version) VALUES (20241029151959);
INSERT INTO public."schema_migrations" (version) VALUES (20241030141825);
INSERT INTO public."schema_migrations" (version) VALUES (20241104061632);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not part of this PR?

INSERT INTO public."schema_migrations" (version) VALUES (20241104115340);
INSERT INTO public."schema_migrations" (version) VALUES (20241108112754);
INSERT INTO public."schema_migrations" (version) VALUES (20241112094924);
INSERT INTO public."schema_migrations" (version) VALUES (20241114140339);
INSERT INTO public."schema_migrations" (version) VALUES (20241114141110);
INSERT INTO public."schema_migrations" (version) VALUES (20241116104556);
INSERT INTO public."schema_migrations" (version) VALUES (20241126110304);
61 changes: 61 additions & 0 deletions test/sanbase_web/graphql/user/user_api_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -384,4 +384,65 @@ defmodule SanbaseWeb.Graphql.UserApiTest do
|> json_response(200)
|> get_in(["data", "currentUser", "isModerator"])
end

describe "Update user profile" do
test "successfully updates profile fields", %{conn: conn} do
mutation = """
mutation {
updateUserProfile(
description: "Test description"
websiteLink: "https://example.com"
twitterLink: "https://twitter.com/test"
) {
description
websiteLink
twitterLink
}
}
"""

result = execute_mutation(conn, mutation, "updateUserProfile")

assert result["description"] == "Test description"
assert result["websiteLink"] == "https://example.com"
assert result["twitterLink"] == "https://twitter.com/test"
end

test "can update individual fields", %{conn: conn} do
mutation = """
mutation {
updateUserProfile(description: "Only description updated") {
description
websiteLink
twitterLink
}
}
"""

result = execute_mutation(conn, mutation, "updateUserProfile")

assert result["description"] == "Only description updated"
assert result["websiteLink"] == nil
assert result["twitterLink"] == nil
end

test "invalid URL format returns error", %{conn: conn} do
mutation = """
mutation {
updateUserProfile(websiteLink: "invalid-url") {
websiteLink
}
}
"""

result =
conn
|> post("/graphql", mutation_skeleton(mutation))

error = json_response(result, 200)["errors"] |> hd()

assert error["details"] == %{"website_link" => ["Invalid URL"]}
assert error["message"] == "Cannot update user profile"
end
end
end