diff --git a/assets/src/generated/graphql.ts b/assets/src/generated/graphql.ts index 3e422111f..3a37e135f 100644 --- a/assets/src/generated/graphql.ts +++ b/assets/src/generated/graphql.ts @@ -5370,6 +5370,7 @@ export type RootMutationType = { createStackDefinition?: Maybe; createThread?: Maybe; createUpgradePolicy?: Maybe; + createUser?: Maybe; createWebhook?: Maybe; deleteAccessToken?: Maybe; deleteCatalog?: Maybe; @@ -5792,6 +5793,11 @@ export type RootMutationTypeCreateUpgradePolicyArgs = { }; +export type RootMutationTypeCreateUserArgs = { + attributes: UserAttributes; +}; + + export type RootMutationTypeCreateWebhookArgs = { attributes: WebhookAttributes; }; diff --git a/go/client/client.go b/go/client/client.go index 2ba3b11e0..9515897d1 100644 --- a/go/client/client.go +++ b/go/client/client.go @@ -188,6 +188,7 @@ type ConsoleClient interface { DeleteAccessToken(ctx context.Context, token string, interceptors ...clientv2.RequestInterceptor) (*DeleteAccessToken, error) SaveUpgradeInsights(ctx context.Context, insights []*UpgradeInsightAttributes, interceptors ...clientv2.RequestInterceptor) (*SaveUpgradeInsights, error) GetUser(ctx context.Context, email string, interceptors ...clientv2.RequestInterceptor) (*GetUser, error) + CreateUser(ctx context.Context, attributes UserAttributes, interceptors ...clientv2.RequestInterceptor) (*CreateUser, error) UpdateUser(ctx context.Context, id *string, attributes UserAttributes, interceptors ...clientv2.RequestInterceptor) (*UpdateUser, error) DeleteUser(ctx context.Context, id string, interceptors ...clientv2.RequestInterceptor) (*DeleteUser, error) AddGroupMember(ctx context.Context, groupID string, userID string, interceptors ...clientv2.RequestInterceptor) (*AddGroupMember, error) @@ -14238,6 +14239,17 @@ func (t *GetUser) GetUser() *UserFragment { return t.User } +type CreateUser struct { + CreateUser *UserFragment "json:\"createUser,omitempty\" graphql:\"createUser\"" +} + +func (t *CreateUser) GetCreateUser() *UserFragment { + if t == nil { + t = &CreateUser{} + } + return t.CreateUser +} + type UpdateUser struct { UpdateUser *UserFragment "json:\"updateUser,omitempty\" graphql:\"updateUser\"" } @@ -27594,6 +27606,35 @@ func (c *Client) GetUser(ctx context.Context, email string, interceptors ...clie return &res, nil } +const CreateUserDocument = `mutation CreateUser ($attributes: UserAttributes!) { + createUser(attributes: $attributes) { + ... UserFragment + } +} +fragment UserFragment on User { + name + id + email +} +` + +func (c *Client) CreateUser(ctx context.Context, attributes UserAttributes, interceptors ...clientv2.RequestInterceptor) (*CreateUser, error) { + vars := map[string]any{ + "attributes": attributes, + } + + var res CreateUser + if err := c.Client.Post(ctx, "CreateUser", CreateUserDocument, &res, vars, interceptors...); err != nil { + if c.Client.ParseDataWhenErrors { + return &res, err + } + + return nil, err + } + + return &res, nil +} + const UpdateUserDocument = `mutation UpdateUser ($id: ID, $attributes: UserAttributes!) { updateUser(id: $id, attributes: $attributes) { ... UserFragment @@ -27922,6 +27963,7 @@ var DocumentOperationNames = map[string]string{ DeleteAccessTokenDocument: "DeleteAccessToken", SaveUpgradeInsightsDocument: "SaveUpgradeInsights", GetUserDocument: "GetUser", + CreateUserDocument: "CreateUser", UpdateUserDocument: "UpdateUser", DeleteUserDocument: "DeleteUser", AddGroupMemberDocument: "AddGroupMember", diff --git a/go/client/graph/users.graphql b/go/client/graph/users.graphql index a0c8aeb15..7eb185508 100644 --- a/go/client/graph/users.graphql +++ b/go/client/graph/users.graphql @@ -4,6 +4,12 @@ query GetUser($email: String!) { } } +mutation CreateUser($attributes: UserAttributes!) { + createUser(attributes: $attributes) { + ...UserFragment + } +} + mutation UpdateUser($id: ID, $attributes: UserAttributes!) { updateUser(id: $id, attributes: $attributes) { ...UserFragment diff --git a/go/controller/docs/api.md b/go/controller/docs/api.md index a427d9ec2..460dedbc0 100644 --- a/go/controller/docs/api.md +++ b/go/controller/docs/api.md @@ -73,7 +73,7 @@ _Appears in:_ | --- | --- | --- | --- | | `enabled` _boolean_ | Enabled defines whether to enable the AI integration or not. | false | Optional: {}
| | `provider` _[AiProvider](#aiprovider)_ | Provider defines which of the supported LLM providers should be used. | OPENAI | Enum: [OPENAI ANTHROPIC OLLAMA AZURE BEDROCK VERTEX]
Optional: {}
| -| `toolProvider` _[AiProvider](#aiprovider)_ | Provider to use for tool calling, in case you want to use a different LLM more optimized to those tasks | OPENAI | Enum: [OPENAI ANTHROPIC OLLAMA AZURE BEDROCK VERTEX]
Optional: {}
| +| `toolProvider` _[AiProvider](#aiprovider)_ | Provider to use for tool calling, in case you want to use a different LLM more optimized to those tasks | | Enum: [OPENAI ANTHROPIC OLLAMA AZURE BEDROCK VERTEX]
Optional: {}
| | `openAI` _[AIProviderSettings](#aiprovidersettings)_ | OpenAI holds the OpenAI provider configuration. | | Optional: {}
| | `anthropic` _[AIProviderSettings](#aiprovidersettings)_ | Anthropic holds the Anthropic provider configuration. | | Optional: {}
| | `ollama` _[OllamaSettings](#ollamasettings)_ | Ollama holds configuration for a self-hosted Ollama deployment, more details available at https://github.com/ollama/ollama | | Optional: {}
| diff --git a/lib/console/graphql/resolvers/user.ex b/lib/console/graphql/resolvers/user.ex index 815e89ed9..7e81a96f4 100644 --- a/lib/console/graphql/resolvers/user.ex +++ b/lib/console/graphql/resolvers/user.ex @@ -194,6 +194,9 @@ defmodule Console.GraphQl.Resolvers.User do def update_user(%{attributes: attrs}, %{context: %{current_user: user}}), do: Users.update_user(attrs, user) + def create_user(%{attributes: attrs}, _), + do: Users.create_user(attrs) + def delete_user(%{id: id}, %{context: %{current_user: user}}), do: Users.delete_user(id, user) diff --git a/lib/console/graphql/users.ex b/lib/console/graphql/users.ex index d09958b62..2d104e6a6 100644 --- a/lib/console/graphql/users.ex +++ b/lib/console/graphql/users.ex @@ -491,6 +491,14 @@ defmodule Console.GraphQl.Users do safe_resolve &User.create_invite/2 end + field :create_user, :user do + middleware Authenticated + middleware AdminRequired + arg :attributes, non_null(:user_attributes) + + resolve &User.create_user/2 + end + field :create_service_account, :user do middleware Authenticated middleware AdminRequired diff --git a/schema/schema.graphql b/schema/schema.graphql index facc401e4..020ce6262 100644 --- a/schema/schema.graphql +++ b/schema/schema.graphql @@ -494,6 +494,8 @@ type RootMutationType { createInvite(attributes: InviteAttributes!): Invite + createUser(attributes: UserAttributes!): User + createServiceAccount(attributes: ServiceAccountAttributes!): User updateServiceAccount(id: ID!, attributes: ServiceAccountAttributes!): User diff --git a/test/console/graphql/mutations/user_mutations_test.exs b/test/console/graphql/mutations/user_mutations_test.exs index 07b34f436..811c84078 100644 --- a/test/console/graphql/mutations/user_mutations_test.exs +++ b/test/console/graphql/mutations/user_mutations_test.exs @@ -24,6 +24,35 @@ defmodule Console.GraphQl.UserMutationsTest do end end + describe "createUser" do + test "admins can create users" do + {:ok, %{data: %{"createUser" => user}}} = run_query(""" + mutation Create($attrs: UserAttributes!) { + createUser(attributes: $attrs) { + id + email + } + } + """, %{"attrs" => %{"email" => "someone@example.com", "name" => "Some User"}}, %{current_user: admin_user()}) + + assert user["email"] == "someone@example.com" + end + + test "nonadmins cannot create users" do + {:ok, %{errors: [_ | _]}} = run_query(""" + mutation Create($attrs: UserAttributes!) { + createUser(attributes: $attrs) { + id + email + } + } + """, %{"attrs" => %{ + "email" => "someone@example.com", + "name" => "Some User" + }}, %{current_user: insert(:user)}) + end + end + describe "logout" do test "it will wipe refresh tokens" do user = insert(:user)