diff --git a/MINT.postman_collection.json b/MINT.postman_collection.json index a54e5ba180..a81e07696f 100644 --- a/MINT.postman_collection.json +++ b/MINT.postman_collection.json @@ -3217,7 +3217,7 @@ "body": { "mode": "graphql", "graphql": { - "query": "mutation NewMTOMilestone {\n createMTOMilestoneCustom(modelPlanID: \"{{modelPlanID}}\",\n name: \"Uncategorized milestone 2\"\n # mtoCategoryID: \"\"\n) {\n id\n name\n addedFromMilestoneLibrary\n facilitatedBy\n needBy\n status\n riskIndicator\n # isDraftMilestone\n mtoCategoryID\n key\n\n # commonMilestone\n # solutions\n # category\n # subCategory\n }\n}", + "query": "mutation NewMTOMilestone {\n createMTOMilestoneCustom(modelPlanID: \"{{modelPlanID}}\",\n name: \"Uncategorized milestone 2\"\n # mtoCategoryID: \"\"\n) {\n id\n name\n addedFromMilestoneLibrary\n facilitatedBy\n needBy\n status\n riskIndicator\n # isDraftMilestone\n mtoCategoryID\n categories {\n category{\n name\n position\n }\n subCategory{\n name\n position\n }\n } \n key\n\n # commonMilestone\n # solutions\n }\n}", "variables": "" } }, @@ -3255,7 +3255,40 @@ "body": { "mode": "graphql", "graphql": { - "query": "mutation NewMTOMilestone {\n createMTOMilestoneCommon(modelPlanID: \"{{modelPlanID}}\",\n commonMilestoneKey: MANAGE_CD\n) {\n id\n name\n key\n addedFromMilestoneLibrary\n facilitatedBy\n needBy\n status\n riskIndicator\n # isDraftMilestone\n mtoCategoryID\n\n # commonMilestone\n # solutions\n # category\n # subCategory\n }\n}", + "query": "mutation NewMTOMilestone {\n createMTOMilestoneCommon(modelPlanID: \"{{modelPlanID}}\",\n commonMilestoneKey: MANAGE_CD\n) {\n id\n name\n key\n addedFromMilestoneLibrary\n facilitatedBy\n needBy\n status\n riskIndicator\n # isDraftMilestone\n mtoCategoryID\n categories {\n category{\n name\n position\n }\n subCategory{\n name\n position\n }\n } \n\n # commonMilestone\n # solutions\n }\n}", + "variables": "" + } + }, + "url": { + "raw": "{{url}}", + "host": [ + "{{url}}" + ] + } + }, + "response": [] + }, + { + "name": "GetMTOMilestone", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "graphql", + "graphql": { + "query": "query GetMTOMilestone {\n mtoMilestone(id: \"{{mtoMilestoneID}}\") {\n id\n name\n key\n addedFromMilestoneLibrary\n facilitatedBy\n needBy\n status\n riskIndicator\n # isDraftMilestone\n mtoCategoryID\n categories {\n category{\n name\n position\n }\n subCategory{\n name\n position\n }\n } \n\n # commonMilestone\n # solutions\n # category\n # subCategory\n }\n}", "variables": "" } }, @@ -3288,7 +3321,7 @@ "body": { "mode": "graphql", "graphql": { - "query": "mutation udpateMTOMilestone {\nupdateMTOMilestone(id: \"{{mtoMilestoneID}}\",\nchanges: {\n mtoCategoryID: \"{{mtoCategoryID}}\"\n facilitatedBy: [IT_LEAD,OTHER],\n riskIndicator: AT_RISK,\n status: IN_PROGRESS\n needBy: \"2024-10-31T17:20:21.959953Z\"\n}\n# commonMilestoneID: \"{{mtoCommonMilestoneID}}\"\n# ,mtoCategoryID: \"{{mtoCategoryID}}\"\n) {\n id\n name\n facilitatedBy\n needBy\n status\n riskIndicator\n # isDraftMilestone\n mtoCategoryID\n modifiedBy\n modifiedDts\n # addedFromMilestoneLibrary\n # commonMilestone\n # solutions\n # category\n # subCategory\n }\n}", + "query": "mutation udpateMTOMilestone {\nupdateMTOMilestone(id: \"{{mtoMilestoneID}}\",\nchanges: {\n mtoCategoryID: \"{{mtoCategoryID}}\"\n facilitatedBy: [IT_LEAD,OTHER],\n riskIndicator: AT_RISK,\n status: IN_PROGRESS\n needBy: \"2024-10-31T17:20:21.959953Z\"\n}\n# commonMilestoneID: \"{{mtoCommonMilestoneID}}\"\n# ,mtoCategoryID: \"{{mtoCategoryID}}\"\n) {\n id\n name\n facilitatedBy\n needBy\n status\n riskIndicator\n # isDraftMilestone\n mtoCategoryID\n categories {\n category{\n name\n }\n subCategory{\n name\n }\n }\n modifiedBy\n modifiedDts\n # addedFromMilestoneLibrary\n # commonMilestone\n # solutions\n }\n}", "variables": "" } }, diff --git a/pkg/graph/generated/generated.go b/pkg/graph/generated/generated.go index a80166ec03..2863f1bce3 100644 --- a/pkg/graph/generated/generated.go +++ b/pkg/graph/generated/generated.go @@ -361,6 +361,11 @@ type ComplexityRoot struct { LockStatus func(childComplexity int) int } + MTOCategories struct { + Category func(childComplexity int) int + SubCategory func(childComplexity int) int + } + MTOCategory struct { ID func(childComplexity int) int IsUncategorized func(childComplexity int) int @@ -415,7 +420,7 @@ type ComplexityRoot struct { MTOMilestone struct { AddedFromMilestoneLibrary func(childComplexity int) int - Category func(childComplexity int) int + Categories func(childComplexity int) int CommonMilestone func(childComplexity int) int CreatedBy func(childComplexity int) int CreatedByUserAccount func(childComplexity int) int @@ -433,7 +438,6 @@ type ComplexityRoot struct { RiskIndicator func(childComplexity int) int Solutions func(childComplexity int) int Status func(childComplexity int) int - SubCategory func(childComplexity int) int } MTOMilestoneTranslation struct { @@ -1879,6 +1883,7 @@ type ComplexityRoot struct { ModelPlanCollection func(childComplexity int, filter model.ModelPlanFilter) int ModelPlansByOperationalSolutionKey func(childComplexity int, operationalSolutionKey models.OperationalSolutionKey) int MostRecentDiscussionRoleSelection func(childComplexity int) int + MtoMilestone func(childComplexity int, id uuid.UUID) int NdaInfo func(childComplexity int) int OperationalNeed func(childComplexity int, id uuid.UUID) int OperationalSolution func(childComplexity int, id uuid.UUID) int @@ -2337,8 +2342,7 @@ type MTOMilestoneResolver interface { CommonMilestone(ctx context.Context, obj *models.MTOMilestone) (*models.MTOCommonMilestone, error) Solutions(ctx context.Context, obj *models.MTOMilestone) ([]*models.MTOSolution, error) - Category(ctx context.Context, obj *models.MTOMilestone) (*models.MTOCategory, error) - SubCategory(ctx context.Context, obj *models.MTOMilestone) (*models.MTOSubcategory, error) + Categories(ctx context.Context, obj *models.MTOMilestone) (*models.MTOCategories, error) } type MTOSolutionResolver interface { FacilitatedBy(ctx context.Context, obj *models.MTOSolution) ([]models.MTOFacilitator, error) @@ -2636,6 +2640,7 @@ type QueryResolver interface { ModelPlan(ctx context.Context, id uuid.UUID) (*models.ModelPlan, error) ModelPlanCollection(ctx context.Context, filter model.ModelPlanFilter) ([]*models.ModelPlan, error) ModelPlansByOperationalSolutionKey(ctx context.Context, operationalSolutionKey models.OperationalSolutionKey) ([]*models.ModelPlanAndPossibleOperationalSolution, error) + MtoMilestone(ctx context.Context, id uuid.UUID) (*models.MTOMilestone, error) NdaInfo(ctx context.Context) (*model.NDAInfo, error) OperationalNeed(ctx context.Context, id uuid.UUID) (*models.OperationalNeed, error) OperationalSolutions(ctx context.Context, operationalNeedID uuid.UUID, includeNotNeeded bool) ([]*models.OperationalSolution, error) @@ -3932,6 +3937,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.LockableSectionLockStatusChanged.LockStatus(childComplexity), true + case "MTOCategories.category": + if e.complexity.MTOCategories.Category == nil { + break + } + + return e.complexity.MTOCategories.Category(childComplexity), true + + case "MTOCategories.subCategory": + if e.complexity.MTOCategories.SubCategory == nil { + break + } + + return e.complexity.MTOCategories.SubCategory(childComplexity), true + case "MTOCategory.id": if e.complexity.MTOCategory.ID == nil { break @@ -4177,12 +4196,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.MTOMilestone.AddedFromMilestoneLibrary(childComplexity), true - case "MTOMilestone.category": - if e.complexity.MTOMilestone.Category == nil { + case "MTOMilestone.categories": + if e.complexity.MTOMilestone.Categories == nil { break } - return e.complexity.MTOMilestone.Category(childComplexity), true + return e.complexity.MTOMilestone.Categories(childComplexity), true case "MTOMilestone.commonMilestone": if e.complexity.MTOMilestone.CommonMilestone == nil { @@ -4303,13 +4322,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.MTOMilestone.Status(childComplexity), true - case "MTOMilestone.subCategory": - if e.complexity.MTOMilestone.SubCategory == nil { - break - } - - return e.complexity.MTOMilestone.SubCategory(childComplexity), true - case "MTOMilestoneTranslation.name": if e.complexity.MTOMilestoneTranslation.Name == nil { break @@ -13653,6 +13665,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Query.MostRecentDiscussionRoleSelection(childComplexity), true + case "Query.mtoMilestone": + if e.complexity.Query.MtoMilestone == nil { + break + } + + args, err := ec.field_Query_mtoMilestone_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Query.MtoMilestone(childComplexity, args["id"].(uuid.UUID)), true + case "Query.ndaInfo": if e.complexity.Query.NdaInfo == nil { break @@ -16840,10 +16864,9 @@ extend type Mutation { @hasRole(role: MINT_USER) }`, BuiltIn: false}, {Name: "../schema/types/mto_category.graphql", Input: ` -# TODO: Add a way to determine order of Categories and subcategories type MTOCategory { # DB Fields - id: UUID! # TODO: If we handle "Uncategorized" as a real category, maybe it won't actually have an ID? + id: UUID! name: String! position: Int! @@ -16855,7 +16878,7 @@ type MTOCategory { type MTOSubcategory { # DB Fields - id: UUID! # TODO: If we handle "Uncategorized" as a real category, maybe it won't actually have an ID? + id: UUID! name: String! position: Int! @@ -16864,6 +16887,15 @@ type MTOSubcategory { milestones: [MTOMilestone!]! } +""" +MTOCategories combines the concept of Category and Subcategory +This allows it to be fetched more conveniently in one resolver +""" +type MTOCategories { + category: MTOCategory! + subCategory: MTOSubcategory! +} + extend type Mutation { """ Allows you to create an MTOCategory or Subcategory if you provide a parent ID. @@ -17095,16 +17127,10 @@ type MTOMilestone { addedFromMilestoneLibrary: Boolean! # Only true when commonMilestone && commonMilestone commonMilestone: MTOCommonMilestone solutions: [MTOSolution!]! - # Category: { - # mtoCategoryID: UUID - # subcategoryID: UUID - # Category: MTOCategory - # subCategory: MTOSubcategory - # } - # TODO (mto) do we need to resolve category? Should we need to resolve it here? Also, - # category: UNION MTOCategory | MTOSubcategory ? - category: MTOCategory! - subCategory: MTOSubcategory! + """ + Category resolves relational category information. + """ + categories: MTOCategories! } input MTOMilestoneChanges @goModel(model: "map[string]interface{}") { @@ -17126,6 +17152,12 @@ extend type Mutation { @hasRole(role: MINT_USER) #TODO (mto) will we need any other mutations here? Can we ever delete? } + +extend type Query { + mtoMilestone(id: UUID!): MTOMilestone! + @hasAnyRole(roles: [MINT_USER, MINT_MAC]) #TODO (mto) is there any reason a MAC user shouldn't query this? +} + `, BuiltIn: false}, {Name: "../schema/types/mto_milestone_translation.graphql", Input: ` """ @@ -22623,6 +22655,21 @@ func (ec *executionContext) field_Query_modelPlansByOperationalSolutionKey_args( return args, nil } +func (ec *executionContext) field_Query_mtoMilestone_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 uuid.UUID + if tmp, ok := rawArgs["id"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) + arg0, err = ec.unmarshalNUUID2githubᚗcomᚋgoogleᚋuuidᚐUUID(ctx, tmp) + if err != nil { + return nil, err + } + } + args["id"] = arg0 + return args, nil +} + func (ec *executionContext) field_Query_operationalNeed_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -31226,6 +31273,118 @@ func (ec *executionContext) fieldContext_LockableSectionLockStatusChanged_action return fc, nil } +func (ec *executionContext) _MTOCategories_category(ctx context.Context, field graphql.CollectedField, obj *models.MTOCategories) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_MTOCategories_category(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Category, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*models.MTOCategory) + fc.Result = res + return ec.marshalNMTOCategory2ᚖgithubᚗcomᚋcmsᚑenterpriseᚋmintᚑappᚋpkgᚋmodelsᚐMTOCategory(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_MTOCategories_category(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "MTOCategories", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_MTOCategory_id(ctx, field) + case "name": + return ec.fieldContext_MTOCategory_name(ctx, field) + case "position": + return ec.fieldContext_MTOCategory_position(ctx, field) + case "isUncategorized": + return ec.fieldContext_MTOCategory_isUncategorized(ctx, field) + case "subCategories": + return ec.fieldContext_MTOCategory_subCategories(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type MTOCategory", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _MTOCategories_subCategory(ctx context.Context, field graphql.CollectedField, obj *models.MTOCategories) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_MTOCategories_subCategory(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.SubCategory, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*models.MTOSubcategory) + fc.Result = res + return ec.marshalNMTOSubcategory2ᚖgithubᚗcomᚋcmsᚑenterpriseᚋmintᚑappᚋpkgᚋmodelsᚐMTOSubcategory(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_MTOCategories_subCategory(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "MTOCategories", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_MTOSubcategory_id(ctx, field) + case "name": + return ec.fieldContext_MTOSubcategory_name(ctx, field) + case "position": + return ec.fieldContext_MTOSubcategory_position(ctx, field) + case "isUncategorized": + return ec.fieldContext_MTOSubcategory_isUncategorized(ctx, field) + case "milestones": + return ec.fieldContext_MTOSubcategory_milestones(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type MTOSubcategory", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _MTOCategory_id(ctx context.Context, field graphql.CollectedField, obj *models.MTOCategory) (ret graphql.Marshaler) { fc, err := ec.fieldContext_MTOCategory_id(ctx, field) if err != nil { @@ -33767,8 +33926,8 @@ func (ec *executionContext) fieldContext_MTOMilestone_solutions(ctx context.Cont return fc, nil } -func (ec *executionContext) _MTOMilestone_category(ctx context.Context, field graphql.CollectedField, obj *models.MTOMilestone) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_MTOMilestone_category(ctx, field) +func (ec *executionContext) _MTOMilestone_categories(ctx context.Context, field graphql.CollectedField, obj *models.MTOMilestone) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_MTOMilestone_categories(ctx, field) if err != nil { return graphql.Null } @@ -33781,7 +33940,7 @@ func (ec *executionContext) _MTOMilestone_category(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.MTOMilestone().Category(rctx, obj) + return ec.resolvers.MTOMilestone().Categories(rctx, obj) }) if err != nil { ec.Error(ctx, err) @@ -33793,12 +33952,12 @@ func (ec *executionContext) _MTOMilestone_category(ctx context.Context, field gr } return graphql.Null } - res := resTmp.(*models.MTOCategory) + res := resTmp.(*models.MTOCategories) fc.Result = res - return ec.marshalNMTOCategory2ᚖgithubᚗcomᚋcmsᚑenterpriseᚋmintᚑappᚋpkgᚋmodelsᚐMTOCategory(ctx, field.Selections, res) + return ec.marshalNMTOCategories2ᚖgithubᚗcomᚋcmsᚑenterpriseᚋmintᚑappᚋpkgᚋmodelsᚐMTOCategories(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_MTOMilestone_category(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_MTOMilestone_categories(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "MTOMilestone", Field: field, @@ -33806,74 +33965,12 @@ func (ec *executionContext) fieldContext_MTOMilestone_category(ctx context.Conte IsResolver: true, Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { switch field.Name { - case "id": - return ec.fieldContext_MTOCategory_id(ctx, field) - case "name": - return ec.fieldContext_MTOCategory_name(ctx, field) - case "position": - return ec.fieldContext_MTOCategory_position(ctx, field) - case "isUncategorized": - return ec.fieldContext_MTOCategory_isUncategorized(ctx, field) - case "subCategories": - return ec.fieldContext_MTOCategory_subCategories(ctx, field) - } - return nil, fmt.Errorf("no field named %q was found under type MTOCategory", field.Name) - }, - } - return fc, nil -} - -func (ec *executionContext) _MTOMilestone_subCategory(ctx context.Context, field graphql.CollectedField, obj *models.MTOMilestone) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_MTOMilestone_subCategory(ctx, field) - if err != nil { - return graphql.Null - } - ctx = graphql.WithFieldContext(ctx, fc) - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - ret = graphql.Null - } - }() - resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { - ctx = rctx // use context from middleware stack in children - return ec.resolvers.MTOMilestone().SubCategory(rctx, obj) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - if !graphql.HasFieldError(ctx, fc) { - ec.Errorf(ctx, "must not be null") - } - return graphql.Null - } - res := resTmp.(*models.MTOSubcategory) - fc.Result = res - return ec.marshalNMTOSubcategory2ᚖgithubᚗcomᚋcmsᚑenterpriseᚋmintᚑappᚋpkgᚋmodelsᚐMTOSubcategory(ctx, field.Selections, res) -} - -func (ec *executionContext) fieldContext_MTOMilestone_subCategory(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { - fc = &graphql.FieldContext{ - Object: "MTOMilestone", - Field: field, - IsMethod: true, - IsResolver: true, - Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { - switch field.Name { - case "id": - return ec.fieldContext_MTOSubcategory_id(ctx, field) - case "name": - return ec.fieldContext_MTOSubcategory_name(ctx, field) - case "position": - return ec.fieldContext_MTOSubcategory_position(ctx, field) - case "isUncategorized": - return ec.fieldContext_MTOSubcategory_isUncategorized(ctx, field) - case "milestones": - return ec.fieldContext_MTOSubcategory_milestones(ctx, field) + case "category": + return ec.fieldContext_MTOCategories_category(ctx, field) + case "subCategory": + return ec.fieldContext_MTOCategories_subCategory(ctx, field) } - return nil, fmt.Errorf("no field named %q was found under type MTOSubcategory", field.Name) + return nil, fmt.Errorf("no field named %q was found under type MTOCategories", field.Name) }, } return fc, nil @@ -34749,10 +34846,8 @@ func (ec *executionContext) fieldContext_MTOSolution_relatedMilestones(ctx conte return ec.fieldContext_MTOMilestone_commonMilestone(ctx, field) case "solutions": return ec.fieldContext_MTOMilestone_solutions(ctx, field) - case "category": - return ec.fieldContext_MTOMilestone_category(ctx, field) - case "subCategory": - return ec.fieldContext_MTOMilestone_subCategory(ctx, field) + case "categories": + return ec.fieldContext_MTOMilestone_categories(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type MTOMilestone", field.Name) }, @@ -35110,10 +35205,8 @@ func (ec *executionContext) fieldContext_MTOSubcategory_milestones(ctx context.C return ec.fieldContext_MTOMilestone_commonMilestone(ctx, field) case "solutions": return ec.fieldContext_MTOMilestone_solutions(ctx, field) - case "category": - return ec.fieldContext_MTOMilestone_category(ctx, field) - case "subCategory": - return ec.fieldContext_MTOMilestone_subCategory(ctx, field) + case "categories": + return ec.fieldContext_MTOMilestone_categories(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type MTOMilestone", field.Name) }, @@ -39244,10 +39337,8 @@ func (ec *executionContext) fieldContext_ModelsToOperationMatrix_milestones(ctx return ec.fieldContext_MTOMilestone_commonMilestone(ctx, field) case "solutions": return ec.fieldContext_MTOMilestone_solutions(ctx, field) - case "category": - return ec.fieldContext_MTOMilestone_category(ctx, field) - case "subCategory": - return ec.fieldContext_MTOMilestone_subCategory(ctx, field) + case "categories": + return ec.fieldContext_MTOMilestone_categories(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type MTOMilestone", field.Name) }, @@ -40797,10 +40888,8 @@ func (ec *executionContext) fieldContext_Mutation_createMTOMilestoneCustom(ctx c return ec.fieldContext_MTOMilestone_commonMilestone(ctx, field) case "solutions": return ec.fieldContext_MTOMilestone_solutions(ctx, field) - case "category": - return ec.fieldContext_MTOMilestone_category(ctx, field) - case "subCategory": - return ec.fieldContext_MTOMilestone_subCategory(ctx, field) + case "categories": + return ec.fieldContext_MTOMilestone_categories(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type MTOMilestone", field.Name) }, @@ -40918,10 +41007,8 @@ func (ec *executionContext) fieldContext_Mutation_createMTOMilestoneCommon(ctx c return ec.fieldContext_MTOMilestone_commonMilestone(ctx, field) case "solutions": return ec.fieldContext_MTOMilestone_solutions(ctx, field) - case "category": - return ec.fieldContext_MTOMilestone_category(ctx, field) - case "subCategory": - return ec.fieldContext_MTOMilestone_subCategory(ctx, field) + case "categories": + return ec.fieldContext_MTOMilestone_categories(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type MTOMilestone", field.Name) }, @@ -41039,10 +41126,8 @@ func (ec *executionContext) fieldContext_Mutation_updateMTOMilestone(ctx context return ec.fieldContext_MTOMilestone_commonMilestone(ctx, field) case "solutions": return ec.fieldContext_MTOMilestone_solutions(ctx, field) - case "category": - return ec.fieldContext_MTOMilestone_category(ctx, field) - case "subCategory": - return ec.fieldContext_MTOMilestone_subCategory(ctx, field) + case "categories": + return ec.fieldContext_MTOMilestone_categories(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type MTOMilestone", field.Name) }, @@ -114888,6 +114973,125 @@ func (ec *executionContext) fieldContext_Query_modelPlansByOperationalSolutionKe return fc, nil } +func (ec *executionContext) _Query_mtoMilestone(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Query_mtoMilestone(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + directive0 := func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return ec.resolvers.Query().MtoMilestone(rctx, fc.Args["id"].(uuid.UUID)) + } + directive1 := func(ctx context.Context) (interface{}, error) { + roles, err := ec.unmarshalNRole2ᚕgithubᚗcomᚋcmsᚑenterpriseᚋmintᚑappᚋpkgᚋgraphᚋmodelᚐRoleᚄ(ctx, []interface{}{"MINT_USER", "MINT_MAC"}) + if err != nil { + return nil, err + } + if ec.directives.HasAnyRole == nil { + return nil, errors.New("directive hasAnyRole is not implemented") + } + return ec.directives.HasAnyRole(ctx, nil, directive0, roles) + } + + tmp, err := directive1(rctx) + if err != nil { + return nil, graphql.ErrorOnPath(ctx, err) + } + if tmp == nil { + return nil, nil + } + if data, ok := tmp.(*models.MTOMilestone); ok { + return data, nil + } + return nil, fmt.Errorf(`unexpected type %T from directive, should be *github.com/cms-enterprise/mint-app/pkg/models.MTOMilestone`, tmp) + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(*models.MTOMilestone) + fc.Result = res + return ec.marshalNMTOMilestone2ᚖgithubᚗcomᚋcmsᚑenterpriseᚋmintᚑappᚋpkgᚋmodelsᚐMTOMilestone(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Query_mtoMilestone(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Query", + Field: field, + IsMethod: true, + IsResolver: true, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "id": + return ec.fieldContext_MTOMilestone_id(ctx, field) + case "name": + return ec.fieldContext_MTOMilestone_name(ctx, field) + case "key": + return ec.fieldContext_MTOMilestone_key(ctx, field) + case "mtoCategoryID": + return ec.fieldContext_MTOMilestone_mtoCategoryID(ctx, field) + case "facilitatedBy": + return ec.fieldContext_MTOMilestone_facilitatedBy(ctx, field) + case "needBy": + return ec.fieldContext_MTOMilestone_needBy(ctx, field) + case "status": + return ec.fieldContext_MTOMilestone_status(ctx, field) + case "riskIndicator": + return ec.fieldContext_MTOMilestone_riskIndicator(ctx, field) + case "isDraft": + return ec.fieldContext_MTOMilestone_isDraft(ctx, field) + case "createdBy": + return ec.fieldContext_MTOMilestone_createdBy(ctx, field) + case "createdByUserAccount": + return ec.fieldContext_MTOMilestone_createdByUserAccount(ctx, field) + case "createdDts": + return ec.fieldContext_MTOMilestone_createdDts(ctx, field) + case "modifiedBy": + return ec.fieldContext_MTOMilestone_modifiedBy(ctx, field) + case "modifiedByUserAccount": + return ec.fieldContext_MTOMilestone_modifiedByUserAccount(ctx, field) + case "modifiedDts": + return ec.fieldContext_MTOMilestone_modifiedDts(ctx, field) + case "addedFromMilestoneLibrary": + return ec.fieldContext_MTOMilestone_addedFromMilestoneLibrary(ctx, field) + case "commonMilestone": + return ec.fieldContext_MTOMilestone_commonMilestone(ctx, field) + case "solutions": + return ec.fieldContext_MTOMilestone_solutions(ctx, field) + case "categories": + return ec.fieldContext_MTOMilestone_categories(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type MTOMilestone", field.Name) + }, + } + defer func() { + if r := recover(); r != nil { + err = ec.Recover(ctx, r) + ec.Error(ctx, err) + } + }() + ctx = graphql.WithFieldContext(ctx, fc) + if fc.Args, err = ec.field_Query_mtoMilestone_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return fc, err + } + return fc, nil +} + func (ec *executionContext) _Query_ndaInfo(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Query_ndaInfo(ctx, field) if err != nil { @@ -135771,6 +135975,50 @@ func (ec *executionContext) _LockableSectionLockStatusChanged(ctx context.Contex return out } +var mTOCategoriesImplementors = []string{"MTOCategories"} + +func (ec *executionContext) _MTOCategories(ctx context.Context, sel ast.SelectionSet, obj *models.MTOCategories) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, mTOCategoriesImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("MTOCategories") + case "category": + out.Values[i] = ec._MTOCategories_category(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "subCategory": + out.Values[i] = ec._MTOCategories_subCategory(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var mTOCategoryImplementors = []string{"MTOCategory"} func (ec *executionContext) _MTOCategory(ctx context.Context, sel ast.SelectionSet, obj *models.MTOCategory) graphql.Marshaler { @@ -136598,43 +136846,7 @@ func (ec *executionContext) _MTOMilestone(ctx context.Context, sel ast.Selection } out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "category": - field := field - - innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - ec.Error(ctx, ec.Recover(ctx, r)) - } - }() - res = ec._MTOMilestone_category(ctx, field, obj) - if res == graphql.Null { - atomic.AddUint32(&fs.Invalids, 1) - } - return res - } - - if field.Deferrable != nil { - dfs, ok := deferred[field.Deferrable.Label] - di := 0 - if ok { - dfs.AddField(field) - di = len(dfs.Values) - 1 - } else { - dfs = graphql.NewFieldSet([]graphql.CollectedField{field}) - deferred[field.Deferrable.Label] = dfs - } - dfs.Concurrently(di, func(ctx context.Context) graphql.Marshaler { - return innerFunc(ctx, dfs) - }) - - // don't run the out.Concurrently() call below - out.Values[i] = graphql.Null - continue - } - - out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) - case "subCategory": + case "categories": field := field innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { @@ -136643,7 +136855,7 @@ func (ec *executionContext) _MTOMilestone(ctx context.Context, sel ast.Selection ec.Error(ctx, ec.Recover(ctx, r)) } }() - res = ec._MTOMilestone_subCategory(ctx, field, obj) + res = ec._MTOMilestone_categories(ctx, field, obj) if res == graphql.Null { atomic.AddUint32(&fs.Invalids, 1) } @@ -149323,6 +149535,28 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) + case "mtoMilestone": + field := field + + innerFunc := func(ctx context.Context, fs *graphql.FieldSet) (res graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + } + }() + res = ec._Query_mtoMilestone(ctx, field) + if res == graphql.Null { + atomic.AddUint32(&fs.Invalids, 1) + } + return res + } + + rrm := func(ctx context.Context) graphql.Marshaler { + return ec.OperationContext.RootResolverMiddleware(ctx, + func(ctx context.Context) graphql.Marshaler { return innerFunc(ctx, out) }) + } + out.Concurrently(i, func(ctx context.Context) graphql.Marshaler { return rrm(innerCtx) }) case "ndaInfo": field := field @@ -155738,6 +155972,20 @@ func (ec *executionContext) marshalNLockableSectionLockStatusChanged2ᚖgithub return ec._LockableSectionLockStatusChanged(ctx, sel, v) } +func (ec *executionContext) marshalNMTOCategories2githubᚗcomᚋcmsᚑenterpriseᚋmintᚑappᚋpkgᚋmodelsᚐMTOCategories(ctx context.Context, sel ast.SelectionSet, v models.MTOCategories) graphql.Marshaler { + return ec._MTOCategories(ctx, sel, &v) +} + +func (ec *executionContext) marshalNMTOCategories2ᚖgithubᚗcomᚋcmsᚑenterpriseᚋmintᚑappᚋpkgᚋmodelsᚐMTOCategories(ctx context.Context, sel ast.SelectionSet, v *models.MTOCategories) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + ec.Errorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._MTOCategories(ctx, sel, v) +} + func (ec *executionContext) marshalNMTOCategory2githubᚗcomᚋcmsᚑenterpriseᚋmintᚑappᚋpkgᚋmodelsᚐMTOCategory(ctx context.Context, sel ast.SelectionSet, v models.MTOCategory) graphql.Marshaler { return ec._MTOCategory(ctx, sel, &v) } @@ -156308,10 +156556,6 @@ func (ec *executionContext) marshalNMTOStatus2githubᚗcomᚋcmsᚑenterpriseᚋ return res } -func (ec *executionContext) marshalNMTOSubcategory2githubᚗcomᚋcmsᚑenterpriseᚋmintᚑappᚋpkgᚋmodelsᚐMTOSubcategory(ctx context.Context, sel ast.SelectionSet, v models.MTOSubcategory) graphql.Marshaler { - return ec._MTOSubcategory(ctx, sel, &v) -} - func (ec *executionContext) marshalNMTOSubcategory2ᚕᚖgithubᚗcomᚋcmsᚑenterpriseᚋmintᚑappᚋpkgᚋmodelsᚐMTOSubcategoryᚄ(ctx context.Context, sel ast.SelectionSet, v []*models.MTOSubcategory) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup diff --git a/pkg/graph/resolvers/mto_category.go b/pkg/graph/resolvers/mto_category.go index 78e7becf2e..e4b4815710 100644 --- a/pkg/graph/resolvers/mto_category.go +++ b/pkg/graph/resolvers/mto_category.go @@ -8,6 +8,7 @@ import ( "go.uber.org/zap" "github.com/cms-enterprise/mint-app/pkg/authentication" + "github.com/cms-enterprise/mint-app/pkg/helpers" "github.com/cms-enterprise/mint-app/pkg/models" "github.com/cms-enterprise/mint-app/pkg/storage" "github.com/cms-enterprise/mint-app/pkg/storage/loaders" @@ -138,3 +139,41 @@ func MTOSubcategoryGetByParentIDLoader(ctx context.Context, modelPlanID uuid.UUI // return while adding an uncategorized record as well return append(dbSubcategories, models.MTOUncategorizedSubcategoryFromArray(modelPlanID, &parentID, dbSubcategories)), nil } + +func MTOCategoryGetByID(ctx context.Context, id uuid.UUID) (*models.MTOCategory, error) { + return loaders.MTOCategory.ByID.Load(ctx, id) +} + +// MTOCategoriesGetByID returns the subcategory and parent category information given an mto category id +// This function first fetches the category by the provided ID. +// Then, if that category is a parent category (and we have nothing else to fetch), returns that category. +// However, if the supplied ID points to a subcategory (i.e. it has a parent ID), then it also fetches the parent category information so both category AND subcategory are returned as part of this resolver. +// We are not doing a larger SQL call that would return both objects, as that would result in less maintainable code +func MTOCategoriesGetByID(ctx context.Context, id *uuid.UUID, modelPlanID uuid.UUID) (*models.MTOCategories, error) { + Categories := &models.MTOCategories{ + Category: models.MTOUncategorized(modelPlanID, nil, 0), + SubCategory: models.MTOUncategorizedSubcategory(modelPlanID, helpers.PointerTo(uuid.Nil), 0), + } + if id == nil { + return Categories, nil + } + immediateCategory, err := MTOCategoryGetByID(ctx, *id) + if err != nil { + return nil, err + } + // Check if it is a parent category, if so early return without fetching parent + if immediateCategory.ParentID == nil { + Categories.Category = immediateCategory + return Categories, nil + } + + // fetch the parent category + parentCategory, err := MTOCategoryGetByID(ctx, *immediateCategory.ParentID) + if err != nil { + return nil, err + } + Categories.SubCategory = immediateCategory.ToSubcategory() + Categories.Category = parentCategory + + return Categories, nil +} diff --git a/pkg/graph/resolvers/mto_milestone.go b/pkg/graph/resolvers/mto_milestone.go index c263231a46..464a690365 100644 --- a/pkg/graph/resolvers/mto_milestone.go +++ b/pkg/graph/resolvers/mto_milestone.go @@ -115,6 +115,11 @@ func MTOMilestoneGetByModelPlanIDLOADER(ctx context.Context, modelPlanID uuid.UU return loaders.MTOMilestone.ByModelPlanID.Load(ctx, modelPlanID) } +// MTOMilestoneGetByIDLOADER returns a mto milestone by it's provided ID +func MTOMilestoneGetByIDLOADER(ctx context.Context, id uuid.UUID) (*models.MTOMilestone, error) { + return loaders.MTOMilestone.ByID.Load(ctx, id) +} + // MTOMilestoneGetByModelPlanIDAndCategoryIDLOADER implements resolver logic to get all MTO milestones by a model plan ID and MTO category ID using a data loader func MTOMilestoneGetByModelPlanIDAndCategoryIDLOADER(ctx context.Context, modelPlanID uuid.UUID, mtoCategoryID uuid.UUID) ([]*models.MTOMilestone, error) { return loaders.MTOMilestone.ByModelPlanIDAndMTOCategoryID.Load(ctx, diff --git a/pkg/graph/resolvers/mto_milestone.resolvers.go b/pkg/graph/resolvers/mto_milestone.resolvers.go index 7491197cf4..f7a584eb5f 100644 --- a/pkg/graph/resolvers/mto_milestone.resolvers.go +++ b/pkg/graph/resolvers/mto_milestone.resolvers.go @@ -37,14 +37,9 @@ func (r *mTOMilestoneResolver) Solutions(ctx context.Context, obj *models.MTOMil panic(fmt.Errorf("not implemented: Solutions - solutions")) } -// Category is the resolver for the category field. -func (r *mTOMilestoneResolver) Category(ctx context.Context, obj *models.MTOMilestone) (*models.MTOCategory, error) { - panic(fmt.Errorf("not implemented: Category - category")) -} - -// SubCategory is the resolver for the subCategory field. -func (r *mTOMilestoneResolver) SubCategory(ctx context.Context, obj *models.MTOMilestone) (*models.MTOSubcategory, error) { - panic(fmt.Errorf("not implemented: SubCategory - subCategory")) +// Categories is the resolver for the categories field. +func (r *mTOMilestoneResolver) Categories(ctx context.Context, obj *models.MTOMilestone) (*models.MTOCategories, error) { + return MTOCategoriesGetByID(ctx, obj.MTOCategoryID, obj.ModelPlanID) } // CreateMTOMilestoneCustom is the resolver for the createMTOMilestoneCustom field. @@ -69,6 +64,11 @@ func (r *mutationResolver) UpdateMTOMilestone(ctx context.Context, id uuid.UUID, return MTOMilestoneUpdate(ctx, logger, principal, r.store, id, changes) } +// MtoMilestone is the resolver for the mtoMilestone field. +func (r *queryResolver) MtoMilestone(ctx context.Context, id uuid.UUID) (*models.MTOMilestone, error) { + return MTOMilestoneGetByIDLOADER(ctx, id) +} + // MTOMilestone returns generated.MTOMilestoneResolver implementation. func (r *Resolver) MTOMilestone() generated.MTOMilestoneResolver { return &mTOMilestoneResolver{r} } diff --git a/pkg/graph/schema/types/mto_category.graphql b/pkg/graph/schema/types/mto_category.graphql index 9172f06d82..5ffb9bfe21 100644 --- a/pkg/graph/schema/types/mto_category.graphql +++ b/pkg/graph/schema/types/mto_category.graphql @@ -1,8 +1,7 @@ -# TODO: Add a way to determine order of Categories and subcategories type MTOCategory { # DB Fields - id: UUID! # TODO: If we handle "Uncategorized" as a real category, maybe it won't actually have an ID? + id: UUID! name: String! position: Int! @@ -14,7 +13,7 @@ type MTOCategory { type MTOSubcategory { # DB Fields - id: UUID! # TODO: If we handle "Uncategorized" as a real category, maybe it won't actually have an ID? + id: UUID! name: String! position: Int! @@ -23,6 +22,15 @@ type MTOSubcategory { milestones: [MTOMilestone!]! } +""" +MTOCategories combines the concept of Category and Subcategory +This allows it to be fetched more conveniently in one resolver +""" +type MTOCategories { + category: MTOCategory! + subCategory: MTOSubcategory! +} + extend type Mutation { """ Allows you to create an MTOCategory or Subcategory if you provide a parent ID. diff --git a/pkg/graph/schema/types/mto_milestone.graphql b/pkg/graph/schema/types/mto_milestone.graphql index 880d944b6c..b4f1775776 100644 --- a/pkg/graph/schema/types/mto_milestone.graphql +++ b/pkg/graph/schema/types/mto_milestone.graphql @@ -36,16 +36,10 @@ type MTOMilestone { addedFromMilestoneLibrary: Boolean! # Only true when commonMilestone && commonMilestone commonMilestone: MTOCommonMilestone solutions: [MTOSolution!]! - # Category: { - # mtoCategoryID: UUID - # subcategoryID: UUID - # Category: MTOCategory - # subCategory: MTOSubcategory - # } - # TODO (mto) do we need to resolve category? Should we need to resolve it here? Also, - # category: UNION MTOCategory | MTOSubcategory ? - category: MTOCategory! - subCategory: MTOSubcategory! + """ + Category resolves relational category information. + """ + categories: MTOCategories! } input MTOMilestoneChanges @goModel(model: "map[string]interface{}") { @@ -67,3 +61,9 @@ extend type Mutation { @hasRole(role: MINT_USER) #TODO (mto) will we need any other mutations here? Can we ever delete? } + +extend type Query { + mtoMilestone(id: UUID!): MTOMilestone! + @hasAnyRole(roles: [MINT_USER, MINT_MAC]) #TODO (mto) is there any reason a MAC user shouldn't query this? +} + diff --git a/pkg/models/mto_category.go b/pkg/models/mto_category.go index 0c9d915ae9..338ebc473c 100644 --- a/pkg/models/mto_category.go +++ b/pkg/models/mto_category.go @@ -22,6 +22,13 @@ type MTOCategory struct { ParentID *uuid.UUID `json:"parent_id" db:"parent_id"` } +// MTOCategories is a struct meant to represent the Category and Subcategory associated with a given MTO Milestone. +// This struct exists to allow us to resolve/fetch both Category and Subcategory at the same time and return them together in a single resolver. +type MTOCategories struct { + Category *MTOCategory `json:"category,omitempty"` + SubCategory *MTOSubcategory `json:"subCategory,omitempty"` +} + func (m MTOCategory) GetPosition() int { return m.Position } diff --git a/pkg/sqlqueries/SQL/mto/category/get_by_id_LOADER.sql b/pkg/sqlqueries/SQL/mto/category/get_by_id_LOADER.sql new file mode 100644 index 0000000000..cdb9bb4cc2 --- /dev/null +++ b/pkg/sqlqueries/SQL/mto/category/get_by_id_LOADER.sql @@ -0,0 +1,17 @@ +WITH QUERIED_IDS AS ( + /*Translate the input to a table */ + SELECT UNNEST(CAST(:ids AS UUID[])) AS id +) + +SELECT + mto_category.id, + mto_category.name, + mto_category.parent_id, + mto_category.model_plan_id, + mto_category.position, + mto_category.created_by, + mto_category.created_dts, + mto_category.modified_by, + mto_category.modified_dts +FROM mto_category +INNER JOIN QUERIED_IDS AS qIDs ON mto_category.id = qIDs.id; diff --git a/pkg/sqlqueries/SQL/mto/milestone/get_by_id_loader.sql b/pkg/sqlqueries/SQL/mto/milestone/get_by_id_loader.sql new file mode 100644 index 0000000000..0c94f78fd4 --- /dev/null +++ b/pkg/sqlqueries/SQL/mto/milestone/get_by_id_loader.sql @@ -0,0 +1,23 @@ +WITH QUERIED_IDS AS ( + /*Translate the input to a table */ + SELECT UNNEST(CAST(:ids AS UUID[])) AS id +) + +SELECT + mto_milestone.id, + mto_milestone.model_plan_id, + mto_milestone.mto_common_milestone_key, + mto_milestone.mto_category_id, + COALESCE(mto_milestone.name, mto_common_milestone.name) AS "name", + mto_milestone.facilitated_by, + mto_milestone.need_by, + mto_milestone.status, + mto_milestone.risk_indicator, + mto_milestone.is_draft, + mto_milestone.created_by, + mto_milestone.created_dts, + mto_milestone.modified_by, + mto_milestone.modified_dts +FROM mto_milestone +INNER JOIN QUERIED_IDS AS qIDs ON mto_milestone.id = qIDs.id +LEFT JOIN mto_common_milestone ON mto_milestone.mto_common_milestone_key = mto_common_milestone.key; diff --git a/pkg/sqlqueries/mto_category.go b/pkg/sqlqueries/mto_category.go index bf2297cf72..ddb7bac3b6 100644 --- a/pkg/sqlqueries/mto_category.go +++ b/pkg/sqlqueries/mto_category.go @@ -14,6 +14,9 @@ var mtoCategoryUpdateSQL string //go:embed SQL/mto/category/get_by_id.sql var mtoCategoryGetByIDSQL string +//go:embed SQL/mto/category/get_by_id_LOADER.sql +var mtoCategoryGetByIDLOADERSQL string + //go:embed SQL/mto/category/get_by_model_plan_id_LOADER.sql var mtoCategoryGetByModelPlanIDLoaderSQL string @@ -28,6 +31,7 @@ type mtoCategoryScripts struct { CreateAllowConflicts string Update string GetByID string + GetByIDLoader string // returns all parent level categories by a model plan ID GetByModelPlanIDLoader string // returns all categories and sub categories by a model plan ID @@ -43,6 +47,7 @@ var MTOCategory = mtoCategoryScripts{ CreateAllowConflicts: mtoCategoryCreateAllowConflictsSQL, Update: mtoCategoryUpdateSQL, GetByID: mtoCategoryGetByIDSQL, + GetByIDLoader: mtoCategoryGetByIDLOADERSQL, GetByModelPlanIDLoader: mtoCategoryGetByModelPlanIDLoaderSQL, AndSubCategoriesGetByModelPlanIDLoader: mtoCategoryAndSubCategoriesGetByModelPlanIDLoaderSQL, GetByParentIDLoader: mtoCategoryGetByParentIDLoaderSQL, diff --git a/pkg/sqlqueries/mto_milestone.go b/pkg/sqlqueries/mto_milestone.go index 7c8dc515a9..188c248e5c 100644 --- a/pkg/sqlqueries/mto_milestone.go +++ b/pkg/sqlqueries/mto_milestone.go @@ -11,6 +11,9 @@ var mtoMilestoneUpdateSQL string //go:embed SQL/mto/milestone/get_by_id.sql var mtoMilestoneGetByIDSQL string +//go:embed SQL/mto/milestone/get_by_id_loader.sql +var mtoMilestoneGetByIDLoaderSQL string + //go:embed SQL/mto/milestone/get_by_model_plan_id_LOADER.sql var mtoMilestoneGetByModelPlanIDLoaderSQL string @@ -18,9 +21,10 @@ var mtoMilestoneGetByModelPlanIDLoaderSQL string var mtoMilestoneGetByModelPlanIDAndCategoryIDLoaderSQL string type mtoMilestoneScripts struct { - Create string - Update string - GetByID string + Create string + Update string + GetByID string + GetByIDLoader string // returns all Milestones by a model plan ID GetByModelPlanIDLoader string // returns all Milestones by a model plan and category @@ -31,6 +35,7 @@ var MTOMilestone = mtoMilestoneScripts{ Create: mtoMilestoneCreateSQL, Update: mtoMilestoneUpdateSQL, GetByID: mtoMilestoneGetByIDSQL, + GetByIDLoader: mtoMilestoneGetByIDLoaderSQL, GetByModelPlanIDLoader: mtoMilestoneGetByModelPlanIDLoaderSQL, GetByModelPlanIDAndCategoryIDLoader: mtoMilestoneGetByModelPlanIDAndCategoryIDLoaderSQL, } diff --git a/pkg/storage/loaders/mto_category.go b/pkg/storage/loaders/mto_category.go index c73b9c3c8b..fbf9b87440 100644 --- a/pkg/storage/loaders/mto_category.go +++ b/pkg/storage/loaders/mto_category.go @@ -19,6 +19,8 @@ type mtoCategoryLoaders struct { AndSubCategoriesByModelPlanID LoaderWrapper[uuid.UUID, []*models.MTOCategory] // ByModelPlanID Gets a list of mto category records at the parent level associated with a model plan by the supplied model plan id. ByModelPlanID LoaderWrapper[uuid.UUID, []*models.MTOCategory] + // By ID returns an MTOCategory by it's id. Note, it could actually be a subcategory as well, but it is returned as a regular category + ByID LoaderWrapper[uuid.UUID, *models.MTOCategory] } // mtoSubcategoryLoaders is a struct that holds LoaderWrappers related to MTO Subcategories @@ -31,6 +33,7 @@ type mtoSubcategoryLoaders struct { var MTOCategory = &mtoCategoryLoaders{ ByModelPlanID: NewLoaderWrapper(batchMTOCategoryGetByModelPlanID), AndSubCategoriesByModelPlanID: NewLoaderWrapper(batchMTOCategoryAndSubCategoriesByModelPlanID), + ByID: NewLoaderWrapper(batchMTOCategoryGetByID), } // MTOSubcategory is the singleton instance of all LoaderWrappers related to MTO Categories @@ -38,6 +41,22 @@ var MTOSubcategory = &mtoSubcategoryLoaders{ ByParentID: NewLoaderWrapper(batchMTOSubcategoryGetByModelPlanID), } +func batchMTOCategoryGetByID(ctx context.Context, ids []uuid.UUID) []*dataloader.Result[*models.MTOCategory] { + loaders, err := Loaders(ctx) + logger := appcontext.ZLogger(ctx) + if err != nil { + return errorPerEachKey[uuid.UUID, *models.MTOCategory](ids, err) + } + data, err := storage.MTOCategoryGetByIDLoader(loaders.DataReader.Store, logger, ids) + if err != nil { + return errorPerEachKey[uuid.UUID, *models.MTOCategory](ids, err) + } + getKeyFunc := func(data *models.MTOCategory) uuid.UUID { + return data.ID + } + return oneToOneDataLoader(ids, data, getKeyFunc) +} + func batchMTOCategoryGetByModelPlanID(ctx context.Context, modelPlanIDs []uuid.UUID) []*dataloader.Result[[]*models.MTOCategory] { loaders, err := Loaders(ctx) logger := appcontext.ZLogger(ctx) diff --git a/pkg/storage/loaders/mto_milestone.go b/pkg/storage/loaders/mto_milestone.go index b106453101..2c645f2bbe 100644 --- a/pkg/storage/loaders/mto_milestone.go +++ b/pkg/storage/loaders/mto_milestone.go @@ -19,13 +19,31 @@ type mtoMilestoneLoaders struct { ByModelPlanID LoaderWrapper[uuid.UUID, []*models.MTOMilestone] // ByModelPlanIDAndMTOCategoryID Gets a list of mto Milestone records associated with a model plan a specific category ByModelPlanIDAndMTOCategoryID LoaderWrapper[storage.MTOMilestoneByModelPlanAndCategoryKey, []*models.MTOMilestone] - // TODO: (mto) do we need to get by ID ever? By anything else? + // By ID returns an MTOCategory by it's id. Note, it could actually be a subcategory as well, but it is returned as a regular category + ByID LoaderWrapper[uuid.UUID, *models.MTOMilestone] } // MTOMilestone is the singleton instance of all LoaderWrappers related to MTO Milestones var MTOMilestone = &mtoMilestoneLoaders{ ByModelPlanID: NewLoaderWrapper(batchMTOMilestoneGetByModelPlanID), ByModelPlanIDAndMTOCategoryID: NewLoaderWrapper(batchMTOMilestoneGetByModelPlanIDAndMTOCategoryID), + ByID: NewLoaderWrapper(batchMTOMilestoneGetByID), +} + +func batchMTOMilestoneGetByID(ctx context.Context, ids []uuid.UUID) []*dataloader.Result[*models.MTOMilestone] { + loaders, err := Loaders(ctx) + logger := appcontext.ZLogger(ctx) + if err != nil { + return errorPerEachKey[uuid.UUID, *models.MTOMilestone](ids, err) + } + data, err := storage.MTOMilestoneGetByIDLoader(loaders.DataReader.Store, logger, ids) + if err != nil { + return errorPerEachKey[uuid.UUID, *models.MTOMilestone](ids, err) + } + getKeyFunc := func(data *models.MTOMilestone) uuid.UUID { + return data.ID + } + return oneToOneDataLoader(ids, data, getKeyFunc) } func batchMTOMilestoneGetByModelPlanID(ctx context.Context, modelPlanIDs []uuid.UUID) []*dataloader.Result[[]*models.MTOMilestone] { diff --git a/pkg/storage/mto_category.go b/pkg/storage/mto_category.go index a91dcbe923..fba3675ed5 100644 --- a/pkg/storage/mto_category.go +++ b/pkg/storage/mto_category.go @@ -12,6 +12,20 @@ import ( "github.com/cms-enterprise/mint-app/pkg/sqlutils" ) +// MTOCategoryGetByIDLoader returns all categories by the provided +func MTOCategoryGetByIDLoader(np sqlutils.NamedPreparer, _ *zap.Logger, ids []uuid.UUID) ([]*models.MTOCategory, error) { + + args := map[string]interface{}{ + "ids": pq.Array(ids), + } + returned, err := sqlutils.SelectProcedure[models.MTOCategory](np, sqlqueries.MTOCategory.GetByIDLoader, args) + if err != nil { + return nil, err + } + return returned, nil + +} + // MTOCategoryGetByModelPlanIDLoader returns all top level categories for a slice of model plan ids func MTOCategoryGetByModelPlanIDLoader(np sqlutils.NamedPreparer, _ *zap.Logger, modelPlanIDs []uuid.UUID) ([]*models.MTOCategory, error) { diff --git a/pkg/storage/mto_milestone.go b/pkg/storage/mto_milestone.go index e30bdfcf2a..ecb3de7017 100644 --- a/pkg/storage/mto_milestone.go +++ b/pkg/storage/mto_milestone.go @@ -12,6 +12,20 @@ import ( "github.com/cms-enterprise/mint-app/pkg/sqlutils" ) +// MTOMilestoneGetByIDLoader returns all milestones by the provided ids +func MTOMilestoneGetByIDLoader(np sqlutils.NamedPreparer, _ *zap.Logger, ids []uuid.UUID) ([]*models.MTOMilestone, error) { + + args := map[string]interface{}{ + "ids": pq.Array(ids), + } + returned, err := sqlutils.SelectProcedure[models.MTOMilestone](np, sqlqueries.MTOMilestone.GetByIDLoader, args) + if err != nil { + return nil, err + } + return returned, nil + +} + // MTOMilestoneGetByModelPlanIDLoader returns all top level categories for a slice of model plan ids func MTOMilestoneGetByModelPlanIDLoader(np sqlutils.NamedPreparer, _ *zap.Logger, modelPlanIDs []uuid.UUID) ([]*models.MTOMilestone, error) { diff --git a/src/gql/generated/graphql.ts b/src/gql/generated/graphql.ts index b42315bbf4..ac956ebb5b 100644 --- a/src/gql/generated/graphql.ts +++ b/src/gql/generated/graphql.ts @@ -716,6 +716,16 @@ export type LockableSectionLockStatusChanged = { lockStatus: LockableSectionLockStatus; }; +/** + * MTOCategories combines the concept of Category and Subcategory + * This allows it to be fetched more conveniently in one resolver + */ +export type MtoCategories = { + __typename: 'MTOCategories'; + category: MtoCategory; + subCategory: MtoSubcategory; +}; + export type MtoCategory = { __typename: 'MTOCategory'; id: Scalars['UUID']['output']; @@ -890,7 +900,8 @@ export type MtoInfoTranslation = { export type MtoMilestone = { __typename: 'MTOMilestone'; addedFromMilestoneLibrary: Scalars['Boolean']['output']; - category: MtoCategory; + /** Category resolves relational category information. */ + categories: MtoCategories; commonMilestone?: Maybe; createdBy: Scalars['UUID']['output']; createdByUserAccount: UserAccount; @@ -912,7 +923,6 @@ export type MtoMilestone = { riskIndicator: MtoRiskIndicator; solutions: Array; status: MtoMilestoneStatus; - subCategory: MtoSubcategory; }; export type MtoMilestoneChanges = { @@ -3767,6 +3777,7 @@ export type Query = { modelPlanCollection: Array; modelPlansByOperationalSolutionKey: Array; mostRecentDiscussionRoleSelection?: Maybe; + mtoMilestone: MtoMilestone; ndaInfo: NdaInfo; operationalNeed: OperationalNeed; operationalSolution: OperationalSolution; @@ -3835,6 +3846,12 @@ export type QueryModelPlansByOperationalSolutionKeyArgs = { }; +/** Query definition for the schema */ +export type QueryMtoMilestoneArgs = { + id: Scalars['UUID']['input']; +}; + + /** Query definition for the schema */ export type QueryOperationalNeedArgs = { id: Scalars['UUID']['input'];