Skip to content

Commit 3950e4f

Browse files
authored
Support create user and team with uuid to integrate with external account systems (#151)
* Support create user with uuid Signed-off-by: kerthcet <kerthcet@gmail.com> * fix test Signed-off-by: kerthcet <kerthcet@gmail.com> * fix test Signed-off-by: kerthcet <kerthcet@gmail.com> * fix test Signed-off-by: kerthcet <kerthcet@gmail.com> * fix test Signed-off-by: kerthcet <kerthcet@gmail.com> --------- Signed-off-by: kerthcet <kerthcet@gmail.com>
1 parent a2c42dc commit 3950e4f

4 files changed

Lines changed: 107 additions & 18 deletions

File tree

alphatrion/server/graphql/resolvers.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ class GraphQLMutations:
294294
def create_user(input: CreateUserInput) -> User:
295295
metadb = runtime.graphql_runtime().metadb
296296
user_id = metadb.create_user(
297+
uuid=uuid.UUID(input.id) if input.id else None,
297298
username=input.username,
298299
email=input.email,
299300
avatar_url=input.avatar_url,
@@ -337,6 +338,7 @@ def update_user(input: UpdateUserInput) -> User:
337338
def create_team(input: CreateTeamInput) -> Team:
338339
metadb = runtime.graphql_runtime().metadb
339340
team_id = metadb.create_team(
341+
uuid=uuid.UUID(input.id) if input.id else None,
340342
name=input.name,
341343
description=input.description,
342344
meta=input.meta,

alphatrion/server/graphql/types.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ class Metric:
145145
# Input types for mutations
146146
@strawberry.input
147147
class CreateUserInput:
148+
id: strawberry.ID | None = None
148149
username: str
149150
email: str
150151
avatar_url: str | None = None
@@ -153,6 +154,7 @@ class CreateUserInput:
153154

154155
@strawberry.input
155156
class CreateTeamInput:
157+
id: strawberry.ID | None = None
156158
name: str
157159
description: str | None = None
158160
meta: JSON | None = None

alphatrion/storage/sqlstore.py

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,25 @@ def __init__(self, db_url: str, init_tables: bool = False):
3131

3232
# ---------- Team APIs ----------
3333

34+
# If uuid is provided, we will use the provided uuid for the new team.
35+
# This is useful for binding with external team management system where
36+
# the team id is already determined.
3437
def create_team(
35-
self, name: str, description: str | None = None, meta: dict | None = None
38+
self,
39+
name: str,
40+
uuid: uuid.UUID | None = None,
41+
description: str | None = None,
42+
meta: dict | None = None,
3643
) -> uuid.UUID:
3744
session = self._session()
3845
new_team = Team(
3946
name=name,
4047
description=description,
4148
meta=meta,
4249
)
50+
if uuid is not None:
51+
new_team.uuid = uuid
52+
4353
session.add(new_team)
4454
session.commit()
4555
team_id = new_team.uuid
@@ -71,27 +81,34 @@ def list_user_teams(self, user_id: uuid.UUID) -> list[Team]:
7181

7282
# ---------- User APIs ----------
7383

84+
# If uuid is provided, we will use the provided uuid for the new user.
85+
# This is useful for binding with external user management system where
86+
# the user id is already determined.
7487
def create_user(
7588
self,
7689
username: str,
7790
email: str,
91+
uuid: uuid.UUID | None = None,
7892
avatar_url: str | None = None,
7993
team_id: uuid.UUID | None = None,
8094
meta: dict | None = None,
8195
) -> uuid.UUID:
96+
user = User(
97+
username=username,
98+
email=email,
99+
avatar_url=avatar_url,
100+
meta=meta,
101+
)
102+
if uuid is not None:
103+
user.uuid = uuid
104+
82105
# If team_id is not provided, we will just create the user
83106
# without any team association.
84107
if team_id is None:
85108
session = self._session()
86-
new_user = User(
87-
username=username,
88-
email=email,
89-
avatar_url=avatar_url,
90-
meta=meta,
91-
)
92-
session.add(new_user)
109+
session.add(user)
93110
session.commit()
94-
user_id = new_user.uuid
111+
user_id = user.uuid
95112
session.close()
96113

97114
return user_id
@@ -100,23 +117,17 @@ def create_user(
100117
# add to the team in a transaction.
101118
session = self._session()
102119
try:
103-
new_user = User(
104-
username=username,
105-
email=email,
106-
avatar_url=avatar_url,
107-
meta=meta,
108-
)
109-
session.add(new_user)
120+
session.add(user)
110121
session.flush() # flush to get the new user's id
111122

112123
new_member = TeamMember(
113-
user_id=new_user.uuid,
124+
user_id=user.uuid,
114125
team_id=team_id,
115126
)
116127
session.add(new_member)
117128

118129
session.commit()
119-
user_id = new_user.uuid
130+
user_id = user.uuid
120131
except Exception as e:
121132
session.rollback()
122133
raise e

tests/integration/server/test_graphql_mutation.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,41 @@ def test_create_team_mutation():
6161
assert team.name == "Test Team"
6262

6363

64+
def test_create_team_mutation_with_uuid():
65+
"""Test creating a team via GraphQL mutation"""
66+
init(init_tables=True)
67+
id = uuid.uuid4() # Generate a UUID to use for the new team
68+
69+
mutation = f"""
70+
mutation {{
71+
createTeam(input: {{
72+
id: "{str(id)}"
73+
name: "Test Team"
74+
description: "A team created via mutation"
75+
meta: {{foo: "bar", count: 42}}
76+
}}) {{
77+
id
78+
name
79+
description
80+
meta
81+
createdAt
82+
updatedAt
83+
totalProjects
84+
totalExperiments
85+
totalRuns
86+
}}
87+
}}
88+
"""
89+
response = schema.execute_sync(
90+
mutation,
91+
variable_values={},
92+
)
93+
assert response.errors is None
94+
assert response.data["createTeam"]["name"] == "Test Team"
95+
# Verify team was actually created in database
96+
assert response.data["createTeam"]["id"] == str(id) # Verify the returned ID matches the provided UUID
97+
98+
6499
def test_create_user_mutation():
65100
"""Test creating a user via GraphQL mutation"""
66101
init(init_tables=True)
@@ -109,6 +144,45 @@ def test_create_user_mutation():
109144
assert user.username == username
110145

111146

147+
def test_create_user_mutation_with_uuid():
148+
"""Test creating a user via GraphQL mutation"""
149+
init(init_tables=True)
150+
id = uuid.uuid4() # Generate a UUID to use for the new user
151+
152+
username = unique_username("testuser")
153+
email = unique_email("testuser")
154+
155+
mutation = f"""
156+
mutation {{
157+
createUser(input: {{
158+
id: "{str(id)}"
159+
username: "{username}"
160+
email: "{email}"
161+
meta: {{role: "engineer", level: "senior"}}
162+
}}) {{
163+
id
164+
username
165+
email
166+
meta
167+
createdAt
168+
updatedAt
169+
teams {{
170+
id
171+
name
172+
}}
173+
}}
174+
}}
175+
"""
176+
response = schema.execute_sync(
177+
mutation,
178+
variable_values={},
179+
)
180+
assert response.errors is None
181+
assert response.data["createUser"]["id"] == str(
182+
id
183+
) # Verify the returned ID matches the provided UUID
184+
185+
112186
def test_add_user_to_team_mutation():
113187
"""Test adding a user to a team via mutation"""
114188
init(init_tables=True)

0 commit comments

Comments
 (0)