diff --git a/service/integration/attribute_values_test.go b/service/integration/attribute_values_test.go index 3183fe3688..4cebaf02d1 100644 --- a/service/integration/attribute_values_test.go +++ b/service/integration/attribute_values_test.go @@ -6,6 +6,7 @@ import ( "sort" "strings" "testing" + "time" "github.com/opentdf/platform/protocol/go/common" "github.com/opentdf/platform/protocol/go/policy" @@ -285,14 +286,19 @@ func (s *AttributeValuesSuite) Test_UpdateAttributeValue() { // create a value attrDef := s.f.GetAttributeKey("example.net/attr/attr1") + start := time.Now().Add(-time.Second) created, err := s.db.PolicyClient.CreateAttributeValue(s.ctx, attrDef.Id, &attributes.CreateAttributeValueRequest{ Value: "created value testing update", Metadata: &common.MetadataMutable{ Labels: labels, }, }) + end := time.Now().Add(time.Second) metadata := created.GetMetadata() updatedAt := metadata.GetUpdatedAt() + createdAt := metadata.GetCreatedAt() + s.True(createdAt.AsTime().After(start)) + s.True(createdAt.AsTime().Before(end)) s.NoError(err) s.NotNil(created) diff --git a/service/integration/attributes_test.go b/service/integration/attributes_test.go index 04ce1cf6d9..844e4fd988 100644 --- a/service/integration/attributes_test.go +++ b/service/integration/attributes_test.go @@ -6,6 +6,7 @@ import ( "log/slog" "strings" "testing" + "time" "github.com/opentdf/platform/protocol/go/common" "github.com/opentdf/platform/protocol/go/policy" @@ -404,9 +405,14 @@ func (s *AttributesSuite) Test_UpdateAttribute() { Labels: labels, }, } + start := time.Now().Add(-time.Second) created, err := s.db.PolicyClient.CreateAttribute(s.ctx, attr) + end := time.Now().Add(time.Second) metadata := created.GetMetadata() updatedAt := metadata.GetUpdatedAt() + createdAt := metadata.GetCreatedAt() + s.True(createdAt.AsTime().After(start)) + s.True(createdAt.AsTime().Before(end)) s.NoError(err) s.NotNil(created) diff --git a/service/integration/namespaces_test.go b/service/integration/namespaces_test.go index 964f41aa01..d905a34eef 100644 --- a/service/integration/namespaces_test.go +++ b/service/integration/namespaces_test.go @@ -6,6 +6,7 @@ import ( "log/slog" "strings" "testing" + "time" "github.com/opentdf/platform/protocol/go/common" "github.com/opentdf/platform/protocol/go/policy" @@ -158,14 +159,19 @@ func (s *NamespacesSuite) Test_UpdateNamespace() { "update": updatedLabel, "new": newLabel, } + start := time.Now().Add(-time.Second) created, err := s.db.PolicyClient.CreateNamespace(s.ctx, &namespaces.CreateNamespaceRequest{ Name: "updating-namespace.com", Metadata: &common.MetadataMutable{ Labels: labels, }, }) + end := time.Now().Add(time.Second) metadata := created.GetMetadata() + createdAt := metadata.GetCreatedAt() updatedAt := metadata.GetUpdatedAt() + s.True(createdAt.AsTime().After(start)) + s.True(createdAt.AsTime().Before(end)) s.NoError(err) s.NotNil(created) diff --git a/service/integration/resource_mappings_test.go b/service/integration/resource_mappings_test.go index d0aee91b65..4ef4392a04 100644 --- a/service/integration/resource_mappings_test.go +++ b/service/integration/resource_mappings_test.go @@ -5,6 +5,7 @@ import ( "fmt" "log/slog" "testing" + "time" "github.com/opentdf/platform/protocol/go/common" "github.com/opentdf/platform/protocol/go/policy/resourcemapping" @@ -192,6 +193,7 @@ func (s *ResourceMappingsSuite) Test_UpdateResourceMapping() { updateTerms := []string{"updated term1", "updated term 2"} attrValue := s.f.GetAttributeValueKey("example.com/attr/attr2/value/value2") + start := time.Now().Add(-time.Second) createdMapping, err := s.db.PolicyClient.CreateResourceMapping(s.ctx, &resourcemapping.CreateResourceMappingRequest{ AttributeValueId: attrValue.Id, Metadata: &common.MetadataMutable{ @@ -199,8 +201,12 @@ func (s *ResourceMappingsSuite) Test_UpdateResourceMapping() { }, Terms: terms, }) + end := time.Now().Add(time.Second) metadata := createdMapping.GetMetadata() updatedAt := metadata.GetUpdatedAt() + createdAt := metadata.GetCreatedAt() + s.True(createdAt.AsTime().After(start)) + s.True(createdAt.AsTime().Before(end)) s.NoError(err) s.NotNil(createdMapping) diff --git a/service/integration/subject_mappings_test.go b/service/integration/subject_mappings_test.go index c4b6e2f565..6e6777dc05 100644 --- a/service/integration/subject_mappings_test.go +++ b/service/integration/subject_mappings_test.go @@ -4,6 +4,7 @@ import ( "context" "log/slog" "testing" + "time" "github.com/opentdf/platform/protocol/go/common" "github.com/opentdf/platform/protocol/go/policy" @@ -203,10 +204,14 @@ func (s *SubjectMappingsSuite) TestUpdateSubjectMapping_Actions() { Actions: []*policy.Action{aTransmit, aCustomUpload}, ExistingSubjectConditionSetId: fixtureScs.Id, } - + start := time.Now().Add(-time.Second) created, err := s.db.PolicyClient.CreateSubjectMapping(context.Background(), newSubjectMapping) + end := time.Now().Add(time.Second) metadata := created.GetMetadata() updatedAt := metadata.GetUpdatedAt() + createdAt := metadata.GetCreatedAt() + s.True(createdAt.AsTime().After(start)) + s.True(createdAt.AsTime().Before(end)) s.Require().NoError(err) s.NotNil(created) @@ -630,10 +635,14 @@ func (s *SubjectMappingsSuite) TestUpdateSubjectConditionSet_NewSubjectSets() { {}, }, } - + start := time.Now().Add(-time.Second) created, err := s.db.PolicyClient.CreateSubjectConditionSet(context.Background(), newConditionSet) + end := time.Now().Add(time.Second) metadata := created.GetMetadata() updatedAt := metadata.GetUpdatedAt() + createdAt := metadata.GetCreatedAt() + s.True(createdAt.AsTime().After(start)) + s.True(createdAt.AsTime().Before(end)) s.Require().NoError(err) s.NotNil(created) diff --git a/service/policy/db/attribute_values.go b/service/policy/db/attribute_values.go index 0765847891..95dd3e72dd 100644 --- a/service/policy/db/attribute_values.go +++ b/service/policy/db/attribute_values.go @@ -145,7 +145,7 @@ func createAttributeValueSql( value, metadata, ). - Suffix("RETURNING id"). + Suffix(createSuffix). ToSql() } @@ -162,6 +162,7 @@ func (c PolicyDBClient) CreateAttributeValue(ctx context.Context, attributeID st value, metadataJSON, ) + if err != nil { return nil, err } @@ -169,7 +170,7 @@ func (c PolicyDBClient) CreateAttributeValue(ctx context.Context, attributeID st var id string if r, err := c.QueryRow(ctx, sql, args); err != nil { return nil, err - } else if err := r.Scan(&id); err != nil { + } else if err := r.Scan(&id, &metadataJSON); err != nil { return nil, db.WrapIfKnownInvalidQueryErr(err) } @@ -194,6 +195,10 @@ func (c PolicyDBClient) CreateAttributeValue(ctx context.Context, attributeID st members = append(members, attr) } + if err = unmarshalMetadata(metadataJSON, metadata); err != nil { + return nil, err + } + // Update FQN c.upsertAttrFqn(ctx, attrFqnUpsertOptions{valueId: id}) @@ -216,7 +221,7 @@ func getAttributeValueSql(id string, opts attributeValueSelectOptions) (string, "'value', vmv.value, " + "'active', vmv.active, " + "'members', vmv.members || ARRAY[]::UUID[], " + - getMetadataField("vmv", true) + + constructMetadata("vmv", true) + "'attribute', JSON_BUILD_OBJECT(" + "'id', vmv.attribute_definition_id )" if opts.withFqn { @@ -228,7 +233,7 @@ func getAttributeValueSql(id string, opts attributeValueSelectOptions) (string, "av.value", "av.active", members, - getMetadataField("av", false), + constructMetadata("av", false), "av.attribute_definition_id", } if opts.withFqn { @@ -281,7 +286,7 @@ func listAttributeValuesSql(attribute_id string, opts attributeValueSelectOption "'value', vmv.value, " + "'active', vmv.active, " + "'members', vmv.members || ARRAY[]::UUID[], " + - getMetadataField("vmv", true) + + constructMetadata("vmv", true) + "'attribute', JSON_BUILD_OBJECT(" + "'id', vmv.attribute_definition_id )" if opts.withFqn { @@ -293,7 +298,7 @@ func listAttributeValuesSql(attribute_id string, opts attributeValueSelectOption "av.value", "av.active", members, - getMetadataField("av", false), + constructMetadata("av", false), "av.attribute_definition_id", } if opts.withFqn { @@ -352,7 +357,7 @@ func listAllAttributeValuesSql(opts attributeValueSelectOptions) (string, []inte "'value', vmv.value, " + "'active', vmv.active, " + "'members', vmv.members || ARRAY[]::UUID[], " + - getMetadataField("vmv", true) + + constructMetadata("vmv", true) + "'attribute', JSON_BUILD_OBJECT(" + "'id', vmv.attribute_definition_id )" if opts.withFqn { @@ -364,7 +369,7 @@ func listAllAttributeValuesSql(opts attributeValueSelectOptions) (string, []inte "av.value", "av.active", members, - getMetadataField("av", false), + constructMetadata("av", false), "av.attribute_definition_id", } if opts.withFqn { diff --git a/service/policy/db/attributes.go b/service/policy/db/attributes.go index fe88a862cb..81f973c910 100644 --- a/service/policy/db/attributes.go +++ b/service/policy/db/attributes.go @@ -80,7 +80,7 @@ func attributesSelect(opts attributesSelectOptions) sq.SelectBuilder { t.Field("id"), t.Field("name"), t.Field("rule"), - getMetadataField(t.Name(), false), + constructMetadata(t.Name(), false), t.Field("namespace_id"), t.Field("active"), nt.Field("name"), @@ -152,10 +152,10 @@ func attributesSelect(opts attributesSelectOptions) sq.SelectBuilder { "JSON_AGG(JSON_BUILD_OBJECT(" + "'id', " + smT.Field("id") + "," + "'actions', " + smT.Field("actions") + "," + - getMetadataField(smT.Name(), true) + + constructMetadata(smT.Name(), true) + "'subject_condition_set', JSON_BUILD_OBJECT(" + "'id', " + scsT.Field("id") + "," + - getMetadataField(scsT.Name(), true) + + constructMetadata(scsT.Name(), true) + "'subject_sets', " + scsT.Field("condition") + ")" + ")) AS sub_maps_arr " + @@ -468,7 +468,7 @@ func createAttributeSql(namespaceId string, name string, rule string, metadata [ Insert(t.Name()). Columns("namespace_id", "name", "rule", "metadata"). Values(namespaceId, name, rule, metadata). - Suffix("RETURNING \"id\""). + Suffix(createSuffix). ToSql() } @@ -488,10 +488,14 @@ func (c PolicyDBClient) CreateAttribute(ctx context.Context, r *attributes.Creat var id string if r, err := c.QueryRow(ctx, sql, args); err != nil { return nil, err - } else if err := r.Scan(&id); err != nil { + } else if err := r.Scan(&id, &metadataJSON); err != nil { return nil, db.WrapIfKnownInvalidQueryErr(err) } + if err = unmarshalMetadata(metadataJSON, metadata); err != nil { + return nil, err + } + // Update the FQN c.upsertAttrFqn(ctx, attrFqnUpsertOptions{attributeId: id}) diff --git a/service/policy/db/namespaces.go b/service/policy/db/namespaces.go index 75dd5e843a..3562df40b7 100644 --- a/service/policy/db/namespaces.go +++ b/service/policy/db/namespaces.go @@ -77,7 +77,7 @@ func getNamespaceSql(id string, opts namespaceSelectOptions) (string, []interfac t.Field("id"), t.Field("name"), t.Field("active"), - getMetadataField("", false), + constructMetadata("", false), } if opts.withFqn { @@ -126,7 +126,7 @@ func listNamespacesSql(opts namespaceSelectOptions) (string, []interface{}, erro t.Field("id"), t.Field("name"), t.Field("active"), - getMetadataField("", false), + constructMetadata("", false), } if opts.withFqn { @@ -180,7 +180,7 @@ func createNamespaceSql(name string, metadata []byte) (string, []interface{}, er Insert(t.Name()). Columns("name", "metadata"). Values(name, metadata). - Suffix("RETURNING \"id\""). + Suffix(createSuffix). ToSql() } @@ -199,10 +199,14 @@ func (c PolicyDBClient) CreateNamespace(ctx context.Context, r *namespaces.Creat var id string if r, e := c.QueryRow(ctx, sql, args); e != nil { return nil, e - } else if e := r.Scan(&id); e != nil { + } else if e = r.Scan(&id, &metadataJSON); e != nil { return nil, db.WrapIfKnownInvalidQueryErr(e) } + if err = unmarshalMetadata(metadataJSON, m); err != nil { + return nil, err + } + // Update FQN c.upsertAttrFqn(ctx, attrFqnUpsertOptions{namespaceId: id}) diff --git a/service/policy/db/resource_mapping.go b/service/policy/db/resource_mapping.go index 02659fd3fc..756f7db77d 100644 --- a/service/policy/db/resource_mapping.go +++ b/service/policy/db/resource_mapping.go @@ -79,7 +79,7 @@ func resourceMappingSelect() sq.SelectBuilder { ")) FILTER (WHERE vmv.id IS NOT NULL ), '[]')" return db.NewStatementBuilder().Select( t.Field("id"), - getMetadataField(t.Name(), false), + constructMetadata(t.Name(), false), t.Field("terms"), "JSON_BUILD_OBJECT("+ "'id', av.id,"+ @@ -111,7 +111,7 @@ func createResourceMappingSQL(attributeValueID string, metadata []byte, terms [] metadata, terms, ). - Suffix("RETURNING \"id\""). + Suffix(createSuffix). ToSql() } @@ -132,7 +132,7 @@ func (c PolicyDBClient) CreateResourceMapping(ctx context.Context, r *resourcema } var id string - if err := row.Scan(&id); err != nil { + if err := row.Scan(&id, &metadataJSON); err != nil { return nil, db.WrapIfKnownInvalidQueryErr(err) } @@ -142,6 +142,10 @@ func (c PolicyDBClient) CreateResourceMapping(ctx context.Context, r *resourcema return nil, db.WrapIfKnownInvalidQueryErr(err) } + if err = unmarshalMetadata(metadataJSON, metadata); err != nil { + return nil, err + } + return &policy.ResourceMapping{ Id: id, Metadata: metadata, diff --git a/service/policy/db/subject_mappings.go b/service/policy/db/subject_mappings.go index 54adca8b79..b3583fc32c 100644 --- a/service/policy/db/subject_mappings.go +++ b/service/policy/db/subject_mappings.go @@ -105,7 +105,7 @@ func subjectConditionSetSelect() sq.SelectBuilder { t := Tables.SubjectConditionSet return db.NewStatementBuilder().Select( t.Field("id"), - getMetadataField("", false), + constructMetadata("", false), t.Field("condition"), ) } @@ -171,10 +171,10 @@ func subjectMappingSelect() sq.SelectBuilder { return db.NewStatementBuilder().Select( t.Field("id"), t.Field("actions"), - getMetadataField(t.Name(), false), + constructMetadata(t.Name(), false), "JSON_BUILD_OBJECT("+ "'id', "+scsT.Field("id")+", "+ - getMetadataField(scsT.Name(), true)+ + constructMetadata(scsT.Name(), true)+ "'subject_sets', "+scsT.Field("condition")+ ") AS subject_condition_set", "JSON_BUILD_OBJECT("+ @@ -303,7 +303,7 @@ func createSubjectConditionSetSql(subjectSets []*policy.SubjectSet, metadataJSON Insert(t.Name()). Columns(columns...). Values(values...). - Suffix("RETURNING \"id\""). + Suffix(createSuffix). ToSql() } @@ -324,9 +324,14 @@ func (c PolicyDBClient) CreateSubjectConditionSet(ctx context.Context, s *subjec if err != nil { return nil, err } - if err = r.Scan(&id); err != nil { + if err = r.Scan(&id, &metadataJSON); err != nil { return nil, db.WrapIfKnownInvalidQueryErr(err) } + + if err = unmarshalMetadata(metadataJSON, m); err != nil { + return nil, err + } + return &policy.SubjectConditionSet{ Id: id, SubjectSets: s.GetSubjectSets(), @@ -486,7 +491,7 @@ func createSubjectMappingSql(attribute_value_id string, actions []byte, metadata Insert(t.Name()). Columns(columns...). Values(values...). - Suffix("RETURNING \"id\""). + Suffix(createSuffix). ToSql() } @@ -540,10 +545,14 @@ func (c PolicyDBClient) CreateSubjectMapping(ctx context.Context, s *subjectmapp var id string if r, err := c.QueryRow(ctx, sql, args); err != nil { return nil, err - } else if err := r.Scan(&id); err != nil { + } else if err := r.Scan(&id, &metadataJSON); err != nil { return nil, db.WrapIfKnownInvalidQueryErr(err) } + if err = unmarshalMetadata(metadataJSON, m); err != nil { + return nil, err + } + return &policy.SubjectMapping{ Id: id, AttributeValue: &policy.Value{ diff --git a/service/policy/db/utils.go b/service/policy/db/utils.go index 8da7f8544e..4569aa70d0 100644 --- a/service/policy/db/utils.go +++ b/service/policy/db/utils.go @@ -1,6 +1,13 @@ package db -func getMetadataField(table string, isJSON bool) string { +import ( + "log/slog" + + "github.com/opentdf/platform/protocol/go/common" + "google.golang.org/protobuf/encoding/protojson" +) + +func constructMetadata(table string, isJSON bool) string { if table != "" { table += "." } @@ -13,3 +20,15 @@ func getMetadataField(table string, isJSON bool) string { } return metadata } + +var createSuffix = "RETURNING id, " + constructMetadata("", false) + +func unmarshalMetadata(metadataJSON []byte, m *common.Metadata) error { + if metadataJSON != nil { + if err := protojson.Unmarshal(metadataJSON, m); err != nil { + slog.Error("could not unmarshal metadata", slog.String("error", err.Error())) + return err + } + } + return nil +}