| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658 |
- package tools
- import (
- "context"
- "encoding/json"
- "fmt"
- "github.com/vektah/gqlparser/v2"
- "github.com/vektah/gqlparser/v2/ast"
- "gogs.dmsc.dev/arp/graph"
- "gogs.dmsc.dev/arp/graph/model"
- )
- // Mutate executes a GraphQL mutation
- func Mutate(ctx context.Context, resolver *graph.Resolver, schema *ast.Schema, args map[string]interface{}) (CallToolResult, error) {
- mutationStr, ok := args["mutation"].(string)
- if !ok {
- return CallToolResult{
- Content: []ContentBlock{
- {Type: "text", Text: "Missing required 'mutation' parameter"},
- },
- IsError: true,
- }, nil
- }
- // Parse variables
- variables := make(map[string]interface{})
- if v, ok := args["variables"].(map[string]interface{}); ok {
- variables = v
- }
- // Parse the mutation
- mutationDoc, err := gqlparser.LoadQuery(schema, mutationStr)
- if err != nil {
- return CallToolResult{
- Content: []ContentBlock{
- {Type: "text", Text: fmt.Sprintf("Mutation parse error: %v", err)},
- },
- IsError: true,
- }, nil
- }
- // Execute each operation
- var results []interface{}
- for _, op := range mutationDoc.Operations {
- if op.Operation != ast.Mutation {
- continue
- }
- result, errMsg := executeMutation(ctx, resolver, schema, mutationDoc, op, variables)
- if errMsg != "" {
- return CallToolResult{
- Content: []ContentBlock{
- {Type: "text", Text: errMsg},
- },
- IsError: true,
- }, nil
- }
- results = append(results, result)
- }
- // Format response
- var responseText string
- if len(results) == 1 {
- bytes, _ := json.MarshalIndent(results[0], "", " ")
- responseText = string(bytes)
- } else {
- bytes, _ := json.MarshalIndent(results, "", " ")
- responseText = string(bytes)
- }
- return CallToolResult{
- Content: []ContentBlock{
- {Type: "text", Text: responseText},
- },
- }, nil
- }
- func executeMutation(ctx context.Context, resolver *graph.Resolver, schema *ast.Schema, doc *ast.QueryDocument, op *ast.OperationDefinition, variables map[string]interface{}) (map[string]interface{}, string) {
- result := make(map[string]interface{})
- for _, sel := range op.SelectionSet {
- field, ok := sel.(*ast.Field)
- if !ok {
- continue
- }
- value, errMsg := resolveMutationField(ctx, resolver, schema, field, variables)
- if errMsg != "" {
- return nil, errMsg
- }
- result[field.Alias] = value
- }
- return result, ""
- }
- func resolveMutationField(ctx context.Context, resolver *graph.Resolver, schema *ast.Schema, field *ast.Field, variables map[string]interface{}) (interface{}, string) {
- // Get field arguments
- args := make(map[string]interface{})
- for _, arg := range field.Arguments {
- value, err := arg.Value.Value(variables)
- if err != nil {
- return nil, fmt.Sprintf("failed to evaluate argument %s: %v", arg.Name, err)
- }
- args[arg.Name] = value
- }
- // Resolve based on field name
- switch field.Name {
- // User mutations
- case "createUser":
- input := parseNewUser(args["input"])
- if input == nil {
- return nil, "missing or invalid input for createUser"
- }
- user, err := resolver.Mutation().CreateUser(ctx, *input)
- if err != nil {
- return nil, err.Error()
- }
- return user, ""
- case "updateUser":
- id, _ := args["id"].(string)
- input := parseUpdateUserInput(args["input"])
- if input == nil {
- return nil, "missing or invalid input for updateUser"
- }
- user, err := resolver.Mutation().UpdateUser(ctx, id, *input)
- if err != nil {
- return nil, err.Error()
- }
- return user, ""
- case "deleteUser":
- id, _ := args["id"].(string)
- user, err := resolver.Mutation().DeleteUser(ctx, id)
- if err != nil {
- return nil, err.Error()
- }
- return user, ""
- // Note mutations
- case "createNote":
- input := parseNewNote(args["input"])
- if input == nil {
- return nil, "missing or invalid input for createNote"
- }
- note, err := resolver.Mutation().CreateNote(ctx, *input)
- if err != nil {
- return nil, err.Error()
- }
- return note, ""
- case "updateNote":
- id, _ := args["id"].(string)
- input := parseUpdateNoteInput(args["input"])
- if input == nil {
- return nil, "missing or invalid input for updateNote"
- }
- note, err := resolver.Mutation().UpdateNote(ctx, id, *input)
- if err != nil {
- return nil, err.Error()
- }
- return note, ""
- case "deleteNote":
- id, _ := args["id"].(string)
- note, err := resolver.Mutation().DeleteNote(ctx, id)
- if err != nil {
- return nil, err.Error()
- }
- return note, ""
- // Role mutations
- case "createRole":
- input := parseNewRole(args["input"])
- if input == nil {
- return nil, "missing or invalid input for createRole"
- }
- role, err := resolver.Mutation().CreateRole(ctx, *input)
- if err != nil {
- return nil, err.Error()
- }
- return role, ""
- case "updateRole":
- id, _ := args["id"].(string)
- input := parseUpdateRoleInput(args["input"])
- if input == nil {
- return nil, "missing or invalid input for updateRole"
- }
- role, err := resolver.Mutation().UpdateRole(ctx, id, *input)
- if err != nil {
- return nil, err.Error()
- }
- return role, ""
- case "deleteRole":
- id, _ := args["id"].(string)
- role, err := resolver.Mutation().DeleteRole(ctx, id)
- if err != nil {
- return nil, err.Error()
- }
- return role, ""
- // Permission mutations
- case "createPermission":
- input := parseNewPermission(args["input"])
- if input == nil {
- return nil, "missing or invalid input for createPermission"
- }
- perm, err := resolver.Mutation().CreatePermission(ctx, *input)
- if err != nil {
- return nil, err.Error()
- }
- return perm, ""
- case "updatePermission":
- id, _ := args["id"].(string)
- input := parseUpdatePermissionInput(args["input"])
- if input == nil {
- return nil, "missing or invalid input for updatePermission"
- }
- perm, err := resolver.Mutation().UpdatePermission(ctx, id, *input)
- if err != nil {
- return nil, err.Error()
- }
- return perm, ""
- case "deletePermission":
- id, _ := args["id"].(string)
- perm, err := resolver.Mutation().DeletePermission(ctx, id)
- if err != nil {
- return nil, err.Error()
- }
- return perm, ""
- // Service mutations
- case "createService":
- input := parseNewService(args["input"])
- if input == nil {
- return nil, "missing or invalid input for createService"
- }
- service, err := resolver.Mutation().CreateService(ctx, *input)
- if err != nil {
- return nil, err.Error()
- }
- return service, ""
- case "updateService":
- id, _ := args["id"].(string)
- input := parseUpdateServiceInput(args["input"])
- if input == nil {
- return nil, "missing or invalid input for updateService"
- }
- service, err := resolver.Mutation().UpdateService(ctx, id, *input)
- if err != nil {
- return nil, err.Error()
- }
- return service, ""
- case "deleteService":
- id, _ := args["id"].(string)
- service, err := resolver.Mutation().DeleteService(ctx, id)
- if err != nil {
- return nil, err.Error()
- }
- return service, ""
- // Task mutations
- case "createTask":
- input := parseNewTask(args["input"])
- if input == nil {
- return nil, "missing or invalid input for createTask"
- }
- task, err := resolver.Mutation().CreateTask(ctx, *input)
- if err != nil {
- return nil, err.Error()
- }
- return task, ""
- case "updateTask":
- id, _ := args["id"].(string)
- input := parseUpdateTaskInput(args["input"])
- if input == nil {
- return nil, "missing or invalid input for updateTask"
- }
- task, err := resolver.Mutation().UpdateTask(ctx, id, *input)
- if err != nil {
- return nil, err.Error()
- }
- return task, ""
- case "deleteTask":
- id, _ := args["id"].(string)
- task, err := resolver.Mutation().DeleteTask(ctx, id)
- if err != nil {
- return nil, err.Error()
- }
- return task, ""
- // TaskStatus mutations
- case "createTaskStatus":
- input := parseNewTaskStatus(args["input"])
- if input == nil {
- return nil, "missing or invalid input for createTaskStatus"
- }
- status, err := resolver.Mutation().CreateTaskStatus(ctx, *input)
- if err != nil {
- return nil, err.Error()
- }
- return status, ""
- case "updateTaskStatus":
- id, _ := args["id"].(string)
- input := parseUpdateTaskStatusInput(args["input"])
- if input == nil {
- return nil, "missing or invalid input for updateTaskStatus"
- }
- status, err := resolver.Mutation().UpdateTaskStatus(ctx, id, *input)
- if err != nil {
- return nil, err.Error()
- }
- return status, ""
- case "deleteTaskStatus":
- id, _ := args["id"].(string)
- status, err := resolver.Mutation().DeleteTaskStatus(ctx, id)
- if err != nil {
- return nil, err.Error()
- }
- return status, ""
- // Message mutations
- case "createMessage":
- input := parseNewMessage(args["input"])
- if input == nil {
- return nil, "missing or invalid input for createMessage"
- }
- msg, err := resolver.Mutation().CreateMessage(ctx, *input)
- if err != nil {
- return nil, err.Error()
- }
- return msg, ""
- case "updateMessage":
- id, _ := args["id"].(string)
- input := parseUpdateMessageInput(args["input"])
- if input == nil {
- return nil, "missing or invalid input for updateMessage"
- }
- msg, err := resolver.Mutation().UpdateMessage(ctx, id, *input)
- if err != nil {
- return nil, err.Error()
- }
- return msg, ""
- case "deleteMessage":
- id, _ := args["id"].(string)
- msg, err := resolver.Mutation().DeleteMessage(ctx, id)
- if err != nil {
- return nil, err.Error()
- }
- return msg, ""
- default:
- return nil, fmt.Sprintf("unknown mutation: %s", field.Name)
- }
- }
- // Helper functions
- func getString(m map[string]interface{}, key string) string {
- if v, ok := m[key]; ok {
- if s, ok := v.(string); ok {
- return s
- }
- }
- return ""
- }
- func parseStringSlice(v interface{}) []string {
- if v == nil {
- return nil
- }
- if arr, ok := v.([]interface{}); ok {
- result := make([]string, 0, len(arr))
- for _, item := range arr {
- if s, ok := item.(string); ok {
- result = append(result, s)
- }
- }
- return result
- }
- if arr, ok := v.([]string); ok {
- return arr
- }
- return nil
- }
- func parseStringPtr(v interface{}) *string {
- if v == nil {
- return nil
- }
- if s, ok := v.(string); ok {
- return &s
- }
- return nil
- }
- // Input parsing functions
- func parseNewUser(v interface{}) *model.NewUser {
- if v == nil {
- return nil
- }
- m, ok := v.(map[string]interface{})
- if !ok {
- return nil
- }
- return &model.NewUser{
- Email: getString(m, "email"),
- Password: getString(m, "password"),
- Roles: parseStringSlice(m["roles"]),
- }
- }
- func parseUpdateUserInput(v interface{}) *model.UpdateUserInput {
- if v == nil {
- return nil
- }
- m, ok := v.(map[string]interface{})
- if !ok {
- return nil
- }
- return &model.UpdateUserInput{
- Email: parseStringPtr(m["email"]),
- Password: parseStringPtr(m["password"]),
- Roles: parseStringSlice(m["roles"]),
- }
- }
- func parseNewNote(v interface{}) *model.NewNote {
- if v == nil {
- return nil
- }
- m, ok := v.(map[string]interface{})
- if !ok {
- return nil
- }
- return &model.NewNote{
- Title: getString(m, "title"),
- Content: getString(m, "content"),
- UserID: getString(m, "userId"),
- ServiceID: getString(m, "serviceId"),
- }
- }
- func parseUpdateNoteInput(v interface{}) *model.UpdateNoteInput {
- if v == nil {
- return nil
- }
- m, ok := v.(map[string]interface{})
- if !ok {
- return nil
- }
- return &model.UpdateNoteInput{
- Title: parseStringPtr(m["title"]),
- Content: parseStringPtr(m["content"]),
- UserID: parseStringPtr(m["userId"]),
- ServiceID: parseStringPtr(m["serviceId"]),
- }
- }
- func parseNewRole(v interface{}) *model.NewRole {
- if v == nil {
- return nil
- }
- m, ok := v.(map[string]interface{})
- if !ok {
- return nil
- }
- return &model.NewRole{
- Name: getString(m, "name"),
- Description: getString(m, "description"),
- Permissions: parseStringSlice(m["permissions"]),
- }
- }
- func parseUpdateRoleInput(v interface{}) *model.UpdateRoleInput {
- if v == nil {
- return nil
- }
- m, ok := v.(map[string]interface{})
- if !ok {
- return nil
- }
- return &model.UpdateRoleInput{
- Name: parseStringPtr(m["name"]),
- Description: parseStringPtr(m["description"]),
- Permissions: parseStringSlice(m["permissions"]),
- }
- }
- func parseNewPermission(v interface{}) *model.NewPermission {
- if v == nil {
- return nil
- }
- m, ok := v.(map[string]interface{})
- if !ok {
- return nil
- }
- return &model.NewPermission{
- Code: getString(m, "code"),
- Description: getString(m, "description"),
- }
- }
- func parseUpdatePermissionInput(v interface{}) *model.UpdatePermissionInput {
- if v == nil {
- return nil
- }
- m, ok := v.(map[string]interface{})
- if !ok {
- return nil
- }
- return &model.UpdatePermissionInput{
- Code: parseStringPtr(m["code"]),
- Description: parseStringPtr(m["description"]),
- }
- }
- func parseNewService(v interface{}) *model.NewService {
- if v == nil {
- return nil
- }
- m, ok := v.(map[string]interface{})
- if !ok {
- return nil
- }
- return &model.NewService{
- Name: getString(m, "name"),
- Description: parseStringPtr(m["description"]),
- CreatedByID: getString(m, "createdById"),
- Participants: parseStringSlice(m["participants"]),
- }
- }
- func parseUpdateServiceInput(v interface{}) *model.UpdateServiceInput {
- if v == nil {
- return nil
- }
- m, ok := v.(map[string]interface{})
- if !ok {
- return nil
- }
- return &model.UpdateServiceInput{
- Name: parseStringPtr(m["name"]),
- Description: parseStringPtr(m["description"]),
- CreatedByID: parseStringPtr(m["createdById"]),
- Participants: parseStringSlice(m["participants"]),
- }
- }
- func parseNewTask(v interface{}) *model.NewTask {
- if v == nil {
- return nil
- }
- m, ok := v.(map[string]interface{})
- if !ok {
- return nil
- }
- return &model.NewTask{
- Title: getString(m, "title"),
- Content: getString(m, "content"),
- CreatedByID: getString(m, "createdById"),
- AssigneeID: parseStringPtr(m["assigneeId"]),
- StatusID: parseStringPtr(m["statusId"]),
- DueDate: parseStringPtr(m["dueDate"]),
- Priority: getString(m, "priority"),
- }
- }
- func parseUpdateTaskInput(v interface{}) *model.UpdateTaskInput {
- if v == nil {
- return nil
- }
- m, ok := v.(map[string]interface{})
- if !ok {
- return nil
- }
- return &model.UpdateTaskInput{
- Title: parseStringPtr(m["title"]),
- Content: parseStringPtr(m["content"]),
- CreatedByID: parseStringPtr(m["createdById"]),
- AssigneeID: parseStringPtr(m["assigneeId"]),
- StatusID: parseStringPtr(m["statusId"]),
- DueDate: parseStringPtr(m["dueDate"]),
- Priority: parseStringPtr(m["priority"]),
- }
- }
- func parseNewTaskStatus(v interface{}) *model.NewTaskStatus {
- if v == nil {
- return nil
- }
- m, ok := v.(map[string]interface{})
- if !ok {
- return nil
- }
- return &model.NewTaskStatus{
- Code: getString(m, "code"),
- Label: getString(m, "label"),
- }
- }
- func parseUpdateTaskStatusInput(v interface{}) *model.UpdateTaskStatusInput {
- if v == nil {
- return nil
- }
- m, ok := v.(map[string]interface{})
- if !ok {
- return nil
- }
- return &model.UpdateTaskStatusInput{
- Code: parseStringPtr(m["code"]),
- Label: parseStringPtr(m["label"]),
- }
- }
- func parseNewMessage(v interface{}) *model.NewMessage {
- if v == nil {
- return nil
- }
- m, ok := v.(map[string]interface{})
- if !ok {
- return nil
- }
- return &model.NewMessage{
- Content: getString(m, "content"),
- Receivers: parseStringSlice(m["receivers"]),
- }
- }
- func parseUpdateMessageInput(v interface{}) *model.UpdateMessageInput {
- if v == nil {
- return nil
- }
- m, ok := v.(map[string]interface{})
- if !ok {
- return nil
- }
- return &model.UpdateMessageInput{
- Content: parseStringPtr(m["content"]),
- Receivers: parseStringSlice(m["receivers"]),
- }
- }
|