@@ -7,6 +7,7 @@ directive @goField(
|
||||
scalar CursorKey
|
||||
scalar Void
|
||||
scalar Datetime
|
||||
scalar Upload
|
||||
|
||||
interface Node {
|
||||
id: ID!
|
||||
@@ -74,6 +75,7 @@ type Control implements Node {
|
||||
id: ID!
|
||||
name: String!
|
||||
description: String!
|
||||
state: String!
|
||||
|
||||
tasks(
|
||||
first: Int
|
||||
|
||||
@@ -56,6 +56,7 @@ type ComplexityRoot struct {
|
||||
Description func(childComplexity int) int
|
||||
ID func(childComplexity int) int
|
||||
Name func(childComplexity int) int
|
||||
State func(childComplexity int) int
|
||||
Tasks func(childComplexity int, first *int, after *page.CursorKey, last *int, before *page.CursorKey) int
|
||||
UpdatedAt func(childComplexity int) int
|
||||
}
|
||||
@@ -186,6 +187,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||
|
||||
return e.complexity.Control.Name(childComplexity), true
|
||||
|
||||
case "Control.state":
|
||||
if e.complexity.Control.State == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.Control.State(childComplexity), true
|
||||
|
||||
case "Control.tasks":
|
||||
if e.complexity.Control.Tasks == nil {
|
||||
break
|
||||
@@ -542,6 +550,7 @@ var sources = []*ast.Source{
|
||||
scalar CursorKey
|
||||
scalar Void
|
||||
scalar Datetime
|
||||
scalar Upload
|
||||
|
||||
interface Node {
|
||||
id: ID!
|
||||
@@ -609,6 +618,7 @@ type Control implements Node {
|
||||
id: ID!
|
||||
name: String!
|
||||
description: String!
|
||||
state: String!
|
||||
|
||||
tasks(
|
||||
first: Int
|
||||
@@ -1094,6 +1104,44 @@ func (ec *executionContext) fieldContext_Control_description(_ context.Context,
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Control_state(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_Control_state(ctx, field)
|
||||
if err != nil {
|
||||
return graphql.Null
|
||||
}
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (any, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.State, 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.(string)
|
||||
fc.Result = res
|
||||
return ec.marshalNString2string(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) fieldContext_Control_state(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||
fc = &graphql.FieldContext{
|
||||
Object: "Control",
|
||||
Field: field,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||
return nil, errors.New("field of type String does not have child fields")
|
||||
},
|
||||
}
|
||||
return fc, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Control_tasks(ctx context.Context, field graphql.CollectedField, obj *types.Control) (ret graphql.Marshaler) {
|
||||
fc, err := ec.fieldContext_Control_tasks(ctx, field)
|
||||
if err != nil {
|
||||
@@ -1388,6 +1436,8 @@ func (ec *executionContext) fieldContext_ControlEdge_node(_ context.Context, fie
|
||||
return ec.fieldContext_Control_name(ctx, field)
|
||||
case "description":
|
||||
return ec.fieldContext_Control_description(ctx, field)
|
||||
case "state":
|
||||
return ec.fieldContext_Control_state(ctx, field)
|
||||
case "tasks":
|
||||
return ec.fieldContext_Control_tasks(ctx, field)
|
||||
case "createdAt":
|
||||
@@ -4275,6 +4325,11 @@ func (ec *executionContext) _Control(ctx context.Context, sel ast.SelectionSet,
|
||||
if out.Values[i] == graphql.Null {
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "state":
|
||||
out.Values[i] = ec._Control_state(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
atomic.AddUint32(&out.Invalids, 1)
|
||||
}
|
||||
case "tasks":
|
||||
field := field
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ func NewControl(c *coredata.Control) *Control {
|
||||
ID: c.ID,
|
||||
Name: c.Name,
|
||||
Description: c.Description,
|
||||
State: string(c.State),
|
||||
CreatedAt: c.CreatedAt,
|
||||
UpdatedAt: c.UpdatedAt,
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ type Control struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
State string `json:"state"`
|
||||
Tasks *TaskConnection `json:"tasks"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
|
||||
@@ -54,7 +54,6 @@ func (r *organizationResolver) Frameworks(ctx context.Context, obj *types.Organi
|
||||
|
||||
// Node is the resolver for the node field.
|
||||
func (r *queryResolver) Node(ctx context.Context, id gid.GID) (types.Node, error) {
|
||||
|
||||
switch id.EntityType() {
|
||||
case coredata.OrganizationEntityType:
|
||||
organization, err := r.svc.GetOrganization(ctx, id)
|
||||
|
||||
@@ -34,6 +34,7 @@ type (
|
||||
FrameworkID string
|
||||
Name string
|
||||
Description string
|
||||
State ControlState
|
||||
ContentRef string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
@@ -52,6 +53,7 @@ func (c *Control) scan(r pgx.Row) error {
|
||||
&c.FrameworkID,
|
||||
&c.Name,
|
||||
&c.Description,
|
||||
&c.State,
|
||||
&c.ContentRef,
|
||||
&c.CreatedAt,
|
||||
&c.UpdatedAt,
|
||||
@@ -66,19 +68,34 @@ func (c *Controls) LoadByFrameworkID(
|
||||
cursor *page.Cursor,
|
||||
) error {
|
||||
q := `
|
||||
WITH control_states AS (
|
||||
SELECT
|
||||
control_id,
|
||||
to_state,
|
||||
reason,
|
||||
RANK() OVER w
|
||||
FROM
|
||||
control_state_transitions
|
||||
WINDOW
|
||||
w AS (PARTITION BY control_id ORDER BY created_at DESC)
|
||||
)
|
||||
SELECT
|
||||
id,
|
||||
framework_id,
|
||||
name,
|
||||
description,
|
||||
cs.to_state AS state,
|
||||
content_ref,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
controls
|
||||
INNER JOIN
|
||||
control_states cs ON cs.control_id = controls.id
|
||||
WHERE
|
||||
%s
|
||||
AND framework_id = @framework_id
|
||||
AND cs.rank = 1
|
||||
AND %s
|
||||
`
|
||||
q = fmt.Sprintf(q, scope.SQLFragment(), cursor.SQLFragment())
|
||||
|
||||
16
pkg/probo/coredata/migrations/20250126T141600Z.sql
Normal file
16
pkg/probo/coredata/migrations/20250126T141600Z.sql
Normal file
@@ -0,0 +1,16 @@
|
||||
CREATE TYPE control_state AS ENUM (
|
||||
'NOT_STARTED',
|
||||
'IN_PROGRESS',
|
||||
'NOT_APPLICABLE',
|
||||
'IMPLEMENTED',
|
||||
);
|
||||
|
||||
CREATE TABLE control_state_transitions (
|
||||
id TEXT PRIMARY KEY,
|
||||
control_id TEXT REFERENCES controls(id) NOT NULL,
|
||||
from_state control_state,
|
||||
to_state control_state NOT NULL,
|
||||
reason TEXT,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL
|
||||
);
|
||||
Reference in New Issue
Block a user