summaryrefslogtreecommitdiff
path: root/vendor/github.com/authzed/spicedb/internal/services/v1/reflectionapi.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/authzed/spicedb/internal/services/v1/reflectionapi.go')
-rw-r--r--vendor/github.com/authzed/spicedb/internal/services/v1/reflectionapi.go720
1 files changed, 720 insertions, 0 deletions
diff --git a/vendor/github.com/authzed/spicedb/internal/services/v1/reflectionapi.go b/vendor/github.com/authzed/spicedb/internal/services/v1/reflectionapi.go
new file mode 100644
index 0000000..723a8d3
--- /dev/null
+++ b/vendor/github.com/authzed/spicedb/internal/services/v1/reflectionapi.go
@@ -0,0 +1,720 @@
+package v1
+
+import (
+ "sort"
+ "strings"
+
+ v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
+ "golang.org/x/exp/maps"
+
+ "github.com/authzed/spicedb/pkg/caveats"
+ caveattypes "github.com/authzed/spicedb/pkg/caveats/types"
+ "github.com/authzed/spicedb/pkg/datastore"
+ "github.com/authzed/spicedb/pkg/diff"
+ caveatdiff "github.com/authzed/spicedb/pkg/diff/caveats"
+ nsdiff "github.com/authzed/spicedb/pkg/diff/namespace"
+ "github.com/authzed/spicedb/pkg/namespace"
+ core "github.com/authzed/spicedb/pkg/proto/core/v1"
+ iv1 "github.com/authzed/spicedb/pkg/proto/impl/v1"
+ "github.com/authzed/spicedb/pkg/spiceerrors"
+ "github.com/authzed/spicedb/pkg/tuple"
+ "github.com/authzed/spicedb/pkg/zedtoken"
+)
+
+type schemaFilters struct {
+ filters []*v1.ReflectionSchemaFilter
+}
+
+func newSchemaFilters(filters []*v1.ReflectionSchemaFilter) (*schemaFilters, error) {
+ for _, filter := range filters {
+ if filter.OptionalDefinitionNameFilter != "" {
+ if filter.OptionalCaveatNameFilter != "" {
+ return nil, NewInvalidFilterErr("cannot filter by both definition and caveat name", filter.String())
+ }
+ }
+
+ if filter.OptionalRelationNameFilter != "" {
+ if filter.OptionalDefinitionNameFilter == "" {
+ return nil, NewInvalidFilterErr("relation name match requires definition name match", filter.String())
+ }
+
+ if filter.OptionalPermissionNameFilter != "" {
+ return nil, NewInvalidFilterErr("cannot filter by both relation and permission name", filter.String())
+ }
+ }
+
+ if filter.OptionalPermissionNameFilter != "" {
+ if filter.OptionalDefinitionNameFilter == "" {
+ return nil, NewInvalidFilterErr("permission name match requires definition name match", filter.String())
+ }
+ }
+ }
+
+ return &schemaFilters{filters: filters}, nil
+}
+
+func (sf *schemaFilters) HasNamespaces() bool {
+ if len(sf.filters) == 0 {
+ return true
+ }
+
+ for _, filter := range sf.filters {
+ if filter.OptionalDefinitionNameFilter != "" {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (sf *schemaFilters) HasCaveats() bool {
+ if len(sf.filters) == 0 {
+ return true
+ }
+
+ for _, filter := range sf.filters {
+ if filter.OptionalCaveatNameFilter != "" {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (sf *schemaFilters) HasNamespace(namespaceName string) bool {
+ if len(sf.filters) == 0 {
+ return true
+ }
+
+ hasDefinitionFilter := false
+ for _, filter := range sf.filters {
+ if filter.OptionalDefinitionNameFilter == "" {
+ continue
+ }
+
+ hasDefinitionFilter = true
+ isMatch := strings.HasPrefix(namespaceName, filter.OptionalDefinitionNameFilter)
+ if isMatch {
+ return true
+ }
+ }
+
+ return !hasDefinitionFilter
+}
+
+func (sf *schemaFilters) HasCaveat(caveatName string) bool {
+ if len(sf.filters) == 0 {
+ return true
+ }
+
+ hasCaveatFilter := false
+ for _, filter := range sf.filters {
+ if filter.OptionalCaveatNameFilter == "" {
+ continue
+ }
+
+ hasCaveatFilter = true
+ isMatch := strings.HasPrefix(caveatName, filter.OptionalCaveatNameFilter)
+ if isMatch {
+ return true
+ }
+ }
+
+ return !hasCaveatFilter
+}
+
+func (sf *schemaFilters) HasRelation(namespaceName, relationName string) bool {
+ if len(sf.filters) == 0 {
+ return true
+ }
+
+ hasRelationFilter := false
+ for _, filter := range sf.filters {
+ if filter.OptionalRelationNameFilter == "" {
+ continue
+ }
+
+ hasRelationFilter = true
+ isMatch := strings.HasPrefix(relationName, filter.OptionalRelationNameFilter)
+ if !isMatch {
+ continue
+ }
+
+ isMatch = strings.HasPrefix(namespaceName, filter.OptionalDefinitionNameFilter)
+ if isMatch {
+ return true
+ }
+ }
+
+ return !hasRelationFilter
+}
+
+func (sf *schemaFilters) HasPermission(namespaceName, permissionName string) bool {
+ if len(sf.filters) == 0 {
+ return true
+ }
+
+ hasPermissionFilter := false
+ for _, filter := range sf.filters {
+ if filter.OptionalPermissionNameFilter == "" {
+ continue
+ }
+
+ hasPermissionFilter = true
+ isMatch := strings.HasPrefix(permissionName, filter.OptionalPermissionNameFilter)
+ if !isMatch {
+ continue
+ }
+
+ isMatch = strings.HasPrefix(namespaceName, filter.OptionalDefinitionNameFilter)
+ if isMatch {
+ return true
+ }
+ }
+
+ return !hasPermissionFilter
+}
+
+// convertDiff converts a schema diff into an API response.
+func convertDiff(
+ diff *diff.SchemaDiff,
+ existingSchema *diff.DiffableSchema,
+ comparisonSchema *diff.DiffableSchema,
+ atRevision datastore.Revision,
+ caveatTypeSet *caveattypes.TypeSet,
+) (*v1.DiffSchemaResponse, error) {
+ size := len(diff.AddedNamespaces) + len(diff.RemovedNamespaces) + len(diff.AddedCaveats) + len(diff.RemovedCaveats) + len(diff.ChangedNamespaces) + len(diff.ChangedCaveats)
+ diffs := make([]*v1.ReflectionSchemaDiff, 0, size)
+
+ // Add/remove namespaces.
+ for _, ns := range diff.AddedNamespaces {
+ nsDef, err := namespaceAPIReprForName(ns, comparisonSchema)
+ if err != nil {
+ return nil, err
+ }
+
+ diffs = append(diffs, &v1.ReflectionSchemaDiff{
+ Diff: &v1.ReflectionSchemaDiff_DefinitionAdded{
+ DefinitionAdded: nsDef,
+ },
+ })
+ }
+
+ for _, ns := range diff.RemovedNamespaces {
+ nsDef, err := namespaceAPIReprForName(ns, existingSchema)
+ if err != nil {
+ return nil, err
+ }
+
+ diffs = append(diffs, &v1.ReflectionSchemaDiff{
+ Diff: &v1.ReflectionSchemaDiff_DefinitionRemoved{
+ DefinitionRemoved: nsDef,
+ },
+ })
+ }
+
+ // Add/remove caveats.
+ for _, caveat := range diff.AddedCaveats {
+ caveatDef, err := caveatAPIReprForName(caveat, comparisonSchema, caveatTypeSet)
+ if err != nil {
+ return nil, err
+ }
+
+ diffs = append(diffs, &v1.ReflectionSchemaDiff{
+ Diff: &v1.ReflectionSchemaDiff_CaveatAdded{
+ CaveatAdded: caveatDef,
+ },
+ })
+ }
+
+ for _, caveat := range diff.RemovedCaveats {
+ caveatDef, err := caveatAPIReprForName(caveat, existingSchema, caveatTypeSet)
+ if err != nil {
+ return nil, err
+ }
+
+ diffs = append(diffs, &v1.ReflectionSchemaDiff{
+ Diff: &v1.ReflectionSchemaDiff_CaveatRemoved{
+ CaveatRemoved: caveatDef,
+ },
+ })
+ }
+
+ // Changed namespaces.
+ for nsName, nsDiff := range diff.ChangedNamespaces {
+ for _, delta := range nsDiff.Deltas() {
+ switch delta.Type {
+ case nsdiff.AddedPermission:
+ permission, ok := comparisonSchema.GetRelation(nsName, delta.RelationName)
+ if !ok {
+ return nil, spiceerrors.MustBugf("permission %q not found in namespace %q", delta.RelationName, nsName)
+ }
+
+ perm, err := permissionAPIRepr(permission, nsName, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ diffs = append(diffs, &v1.ReflectionSchemaDiff{
+ Diff: &v1.ReflectionSchemaDiff_PermissionAdded{
+ PermissionAdded: perm,
+ },
+ })
+
+ case nsdiff.AddedRelation:
+ relation, ok := comparisonSchema.GetRelation(nsName, delta.RelationName)
+ if !ok {
+ return nil, spiceerrors.MustBugf("relation %q not found in namespace %q", delta.RelationName, nsName)
+ }
+
+ rel, err := relationAPIRepr(relation, nsName, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ diffs = append(diffs, &v1.ReflectionSchemaDiff{
+ Diff: &v1.ReflectionSchemaDiff_RelationAdded{
+ RelationAdded: rel,
+ },
+ })
+
+ case nsdiff.ChangedPermissionComment:
+ permission, ok := comparisonSchema.GetRelation(nsName, delta.RelationName)
+ if !ok {
+ return nil, spiceerrors.MustBugf("permission %q not found in namespace %q", delta.RelationName, nsName)
+ }
+
+ perm, err := permissionAPIRepr(permission, nsName, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ diffs = append(diffs, &v1.ReflectionSchemaDiff{
+ Diff: &v1.ReflectionSchemaDiff_PermissionDocCommentChanged{
+ PermissionDocCommentChanged: perm,
+ },
+ })
+
+ case nsdiff.ChangedPermissionImpl:
+ permission, ok := comparisonSchema.GetRelation(nsName, delta.RelationName)
+ if !ok {
+ return nil, spiceerrors.MustBugf("permission %q not found in namespace %q", delta.RelationName, nsName)
+ }
+
+ perm, err := permissionAPIRepr(permission, nsName, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ diffs = append(diffs, &v1.ReflectionSchemaDiff{
+ Diff: &v1.ReflectionSchemaDiff_PermissionExprChanged{
+ PermissionExprChanged: perm,
+ },
+ })
+
+ case nsdiff.ChangedRelationComment:
+ relation, ok := comparisonSchema.GetRelation(nsName, delta.RelationName)
+ if !ok {
+ return nil, spiceerrors.MustBugf("relation %q not found in namespace %q", delta.RelationName, nsName)
+ }
+
+ rel, err := relationAPIRepr(relation, nsName, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ diffs = append(diffs, &v1.ReflectionSchemaDiff{
+ Diff: &v1.ReflectionSchemaDiff_RelationDocCommentChanged{
+ RelationDocCommentChanged: rel,
+ },
+ })
+
+ case nsdiff.LegacyChangedRelationImpl:
+ return nil, spiceerrors.MustBugf("legacy relation implementation changes are not supported")
+
+ case nsdiff.NamespaceCommentsChanged:
+ def, err := namespaceAPIReprForName(nsName, comparisonSchema)
+ if err != nil {
+ return nil, err
+ }
+
+ diffs = append(diffs, &v1.ReflectionSchemaDiff{
+ Diff: &v1.ReflectionSchemaDiff_DefinitionDocCommentChanged{
+ DefinitionDocCommentChanged: def,
+ },
+ })
+
+ case nsdiff.RelationAllowedTypeRemoved:
+ relation, ok := comparisonSchema.GetRelation(nsName, delta.RelationName)
+ if !ok {
+ return nil, spiceerrors.MustBugf("relation %q not found in namespace %q", delta.RelationName, nsName)
+ }
+
+ rel, err := relationAPIRepr(relation, nsName, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ diffs = append(diffs, &v1.ReflectionSchemaDiff{
+ Diff: &v1.ReflectionSchemaDiff_RelationSubjectTypeRemoved{
+ RelationSubjectTypeRemoved: &v1.ReflectionRelationSubjectTypeChange{
+ Relation: rel,
+ ChangedSubjectType: typeAPIRepr(delta.AllowedType),
+ },
+ },
+ })
+
+ case nsdiff.RelationAllowedTypeAdded:
+ relation, ok := comparisonSchema.GetRelation(nsName, delta.RelationName)
+ if !ok {
+ return nil, spiceerrors.MustBugf("relation %q not found in namespace %q", delta.RelationName, nsName)
+ }
+
+ rel, err := relationAPIRepr(relation, nsName, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ diffs = append(diffs, &v1.ReflectionSchemaDiff{
+ Diff: &v1.ReflectionSchemaDiff_RelationSubjectTypeAdded{
+ RelationSubjectTypeAdded: &v1.ReflectionRelationSubjectTypeChange{
+ Relation: rel,
+ ChangedSubjectType: typeAPIRepr(delta.AllowedType),
+ },
+ },
+ })
+
+ case nsdiff.RemovedPermission:
+ permission, ok := existingSchema.GetRelation(nsName, delta.RelationName)
+ if !ok {
+ return nil, spiceerrors.MustBugf("relation %q not found in namespace %q", delta.RelationName, nsName)
+ }
+
+ perm, err := permissionAPIRepr(permission, nsName, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ diffs = append(diffs, &v1.ReflectionSchemaDiff{
+ Diff: &v1.ReflectionSchemaDiff_PermissionRemoved{
+ PermissionRemoved: perm,
+ },
+ })
+
+ case nsdiff.RemovedRelation:
+ relation, ok := existingSchema.GetRelation(nsName, delta.RelationName)
+ if !ok {
+ return nil, spiceerrors.MustBugf("relation %q not found in namespace %q", delta.RelationName, nsName)
+ }
+
+ rel, err := relationAPIRepr(relation, nsName, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ diffs = append(diffs, &v1.ReflectionSchemaDiff{
+ Diff: &v1.ReflectionSchemaDiff_RelationRemoved{
+ RelationRemoved: rel,
+ },
+ })
+
+ case nsdiff.NamespaceAdded:
+ return nil, spiceerrors.MustBugf("should be handled above")
+
+ case nsdiff.NamespaceRemoved:
+ return nil, spiceerrors.MustBugf("should be handled above")
+
+ default:
+ return nil, spiceerrors.MustBugf("unexpected delta type %v", delta.Type)
+ }
+ }
+ }
+
+ // Changed caveats.
+ for caveatName, caveatDiff := range diff.ChangedCaveats {
+ for _, delta := range caveatDiff.Deltas() {
+ switch delta.Type {
+ case caveatdiff.CaveatCommentsChanged:
+ caveat, err := caveatAPIReprForName(caveatName, comparisonSchema, caveatTypeSet)
+ if err != nil {
+ return nil, err
+ }
+
+ diffs = append(diffs, &v1.ReflectionSchemaDiff{
+ Diff: &v1.ReflectionSchemaDiff_CaveatDocCommentChanged{
+ CaveatDocCommentChanged: caveat,
+ },
+ })
+
+ case caveatdiff.AddedParameter:
+ paramDef, err := caveatAPIParamRepr(delta.ParameterName, caveatName, comparisonSchema, caveatTypeSet)
+ if err != nil {
+ return nil, err
+ }
+
+ diffs = append(diffs, &v1.ReflectionSchemaDiff{
+ Diff: &v1.ReflectionSchemaDiff_CaveatParameterAdded{
+ CaveatParameterAdded: paramDef,
+ },
+ })
+
+ case caveatdiff.RemovedParameter:
+ paramDef, err := caveatAPIParamRepr(delta.ParameterName, caveatName, existingSchema, caveatTypeSet)
+ if err != nil {
+ return nil, err
+ }
+
+ diffs = append(diffs, &v1.ReflectionSchemaDiff{
+ Diff: &v1.ReflectionSchemaDiff_CaveatParameterRemoved{
+ CaveatParameterRemoved: paramDef,
+ },
+ })
+
+ case caveatdiff.ParameterTypeChanged:
+ previousParamDef, err := caveatAPIParamRepr(delta.ParameterName, caveatName, existingSchema, caveatTypeSet)
+ if err != nil {
+ return nil, err
+ }
+
+ paramDef, err := caveatAPIParamRepr(delta.ParameterName, caveatName, comparisonSchema, caveatTypeSet)
+ if err != nil {
+ return nil, err
+ }
+
+ diffs = append(diffs, &v1.ReflectionSchemaDiff{
+ Diff: &v1.ReflectionSchemaDiff_CaveatParameterTypeChanged{
+ CaveatParameterTypeChanged: &v1.ReflectionCaveatParameterTypeChange{
+ Parameter: paramDef,
+ PreviousType: previousParamDef.Type,
+ },
+ },
+ })
+
+ case caveatdiff.CaveatExpressionChanged:
+ caveat, err := caveatAPIReprForName(caveatName, comparisonSchema, caveatTypeSet)
+ if err != nil {
+ return nil, err
+ }
+
+ diffs = append(diffs, &v1.ReflectionSchemaDiff{
+ Diff: &v1.ReflectionSchemaDiff_CaveatExprChanged{
+ CaveatExprChanged: caveat,
+ },
+ })
+
+ case caveatdiff.CaveatAdded:
+ return nil, spiceerrors.MustBugf("should be handled above")
+
+ case caveatdiff.CaveatRemoved:
+ return nil, spiceerrors.MustBugf("should be handled above")
+
+ default:
+ return nil, spiceerrors.MustBugf("unexpected delta type %v", delta.Type)
+ }
+ }
+ }
+
+ return &v1.DiffSchemaResponse{
+ Diffs: diffs,
+ ReadAt: zedtoken.MustNewFromRevision(atRevision),
+ }, nil
+}
+
+// namespaceAPIReprForName builds an API representation of a namespace.
+func namespaceAPIReprForName(namespaceName string, schema *diff.DiffableSchema) (*v1.ReflectionDefinition, error) {
+ nsDef, ok := schema.GetNamespace(namespaceName)
+ if !ok {
+ return nil, spiceerrors.MustBugf("namespace %q not found in schema", namespaceName)
+ }
+
+ return namespaceAPIRepr(nsDef, nil)
+}
+
+func namespaceAPIRepr(nsDef *core.NamespaceDefinition, schemaFilters *schemaFilters) (*v1.ReflectionDefinition, error) {
+ if schemaFilters != nil && !schemaFilters.HasNamespace(nsDef.Name) {
+ return nil, nil
+ }
+
+ relations := make([]*v1.ReflectionRelation, 0, len(nsDef.Relation))
+ permissions := make([]*v1.ReflectionPermission, 0, len(nsDef.Relation))
+
+ for _, rel := range nsDef.Relation {
+ if namespace.GetRelationKind(rel) == iv1.RelationMetadata_PERMISSION {
+ permission, err := permissionAPIRepr(rel, nsDef.Name, schemaFilters)
+ if err != nil {
+ return nil, err
+ }
+
+ if permission != nil {
+ permissions = append(permissions, permission)
+ }
+ continue
+ }
+
+ relation, err := relationAPIRepr(rel, nsDef.Name, schemaFilters)
+ if err != nil {
+ return nil, err
+ }
+
+ if relation != nil {
+ relations = append(relations, relation)
+ }
+ }
+
+ comments := namespace.GetComments(nsDef.Metadata)
+ return &v1.ReflectionDefinition{
+ Name: nsDef.Name,
+ Comment: strings.Join(comments, "\n"),
+ Relations: relations,
+ Permissions: permissions,
+ }, nil
+}
+
+// permissionAPIRepr builds an API representation of a permission.
+func permissionAPIRepr(relation *core.Relation, parentDefName string, schemaFilters *schemaFilters) (*v1.ReflectionPermission, error) {
+ if schemaFilters != nil && !schemaFilters.HasPermission(parentDefName, relation.Name) {
+ return nil, nil
+ }
+
+ comments := namespace.GetComments(relation.Metadata)
+ return &v1.ReflectionPermission{
+ Name: relation.Name,
+ Comment: strings.Join(comments, "\n"),
+ ParentDefinitionName: parentDefName,
+ }, nil
+}
+
+// relationAPIRepresentation builds an API representation of a relation.
+func relationAPIRepr(relation *core.Relation, parentDefName string, schemaFilters *schemaFilters) (*v1.ReflectionRelation, error) {
+ if schemaFilters != nil && !schemaFilters.HasRelation(parentDefName, relation.Name) {
+ return nil, nil
+ }
+
+ comments := namespace.GetComments(relation.Metadata)
+
+ var subjectTypes []*v1.ReflectionTypeReference
+ if relation.TypeInformation != nil {
+ subjectTypes = make([]*v1.ReflectionTypeReference, 0, len(relation.TypeInformation.AllowedDirectRelations))
+ for _, subjectType := range relation.TypeInformation.AllowedDirectRelations {
+ typeref := typeAPIRepr(subjectType)
+ subjectTypes = append(subjectTypes, typeref)
+ }
+ }
+
+ return &v1.ReflectionRelation{
+ Name: relation.Name,
+ Comment: strings.Join(comments, "\n"),
+ ParentDefinitionName: parentDefName,
+ SubjectTypes: subjectTypes,
+ }, nil
+}
+
+// typeAPIRepr builds an API representation of a type.
+func typeAPIRepr(subjectType *core.AllowedRelation) *v1.ReflectionTypeReference {
+ typeref := &v1.ReflectionTypeReference{
+ SubjectDefinitionName: subjectType.Namespace,
+ Typeref: &v1.ReflectionTypeReference_IsTerminalSubject{},
+ }
+
+ if subjectType.GetRelation() != tuple.Ellipsis && subjectType.GetRelation() != "" {
+ typeref.Typeref = &v1.ReflectionTypeReference_OptionalRelationName{
+ OptionalRelationName: subjectType.GetRelation(),
+ }
+ } else if subjectType.GetPublicWildcard() != nil {
+ typeref.Typeref = &v1.ReflectionTypeReference_IsPublicWildcard{
+ IsPublicWildcard: true,
+ }
+ }
+
+ if subjectType.GetRequiredCaveat() != nil {
+ typeref.OptionalCaveatName = subjectType.GetRequiredCaveat().CaveatName
+ }
+
+ return typeref
+}
+
+// caveatAPIReprForName builds an API representation of a caveat.
+func caveatAPIReprForName(caveatName string, schema *diff.DiffableSchema, caveatTypeSet *caveattypes.TypeSet) (*v1.ReflectionCaveat, error) {
+ caveatDef, ok := schema.GetCaveat(caveatName)
+ if !ok {
+ return nil, spiceerrors.MustBugf("caveat %q not found in schema", caveatName)
+ }
+
+ return caveatAPIRepr(caveatDef, nil, caveatTypeSet)
+}
+
+// caveatAPIRepr builds an API representation of a caveat.
+func caveatAPIRepr(caveatDef *core.CaveatDefinition, schemaFilters *schemaFilters, caveatTypeSet *caveattypes.TypeSet) (*v1.ReflectionCaveat, error) {
+ if schemaFilters != nil && !schemaFilters.HasCaveat(caveatDef.Name) {
+ return nil, nil
+ }
+
+ parameters := make([]*v1.ReflectionCaveatParameter, 0, len(caveatDef.ParameterTypes))
+ paramNames := maps.Keys(caveatDef.ParameterTypes)
+ sort.Strings(paramNames)
+
+ for _, paramName := range paramNames {
+ paramType, ok := caveatDef.ParameterTypes[paramName]
+ if !ok {
+ return nil, spiceerrors.MustBugf("parameter %q not found in caveat %q", paramName, caveatDef.Name)
+ }
+
+ decoded, err := caveattypes.DecodeParameterType(caveatTypeSet, paramType)
+ if err != nil {
+ return nil, spiceerrors.MustBugf("invalid parameter type on caveat: %v", err)
+ }
+
+ parameters = append(parameters, &v1.ReflectionCaveatParameter{
+ Name: paramName,
+ Type: decoded.String(),
+ ParentCaveatName: caveatDef.Name,
+ })
+ }
+
+ parameterTypes, err := caveattypes.DecodeParameterTypes(caveatTypeSet, caveatDef.ParameterTypes)
+ if err != nil {
+ return nil, spiceerrors.MustBugf("invalid caveat parameters: %v", err)
+ }
+
+ deserializedReflectionression, err := caveats.DeserializeCaveatWithTypeSet(caveatTypeSet, caveatDef.SerializedExpression, parameterTypes)
+ if err != nil {
+ return nil, spiceerrors.MustBugf("invalid caveat expression bytes: %v", err)
+ }
+
+ exprString, err := deserializedReflectionression.ExprString()
+ if err != nil {
+ return nil, spiceerrors.MustBugf("invalid caveat expression: %v", err)
+ }
+
+ comments := namespace.GetComments(caveatDef.Metadata)
+ return &v1.ReflectionCaveat{
+ Name: caveatDef.Name,
+ Comment: strings.Join(comments, "\n"),
+ Parameters: parameters,
+ Expression: exprString,
+ }, nil
+}
+
+// caveatAPIParamRepresentation builds an API representation of a caveat parameter.
+func caveatAPIParamRepr(paramName, parentCaveatName string, schema *diff.DiffableSchema, caveatTypeSet *caveattypes.TypeSet) (*v1.ReflectionCaveatParameter, error) {
+ caveatDef, ok := schema.GetCaveat(parentCaveatName)
+ if !ok {
+ return nil, spiceerrors.MustBugf("caveat %q not found in schema", parentCaveatName)
+ }
+
+ paramType, ok := caveatDef.ParameterTypes[paramName]
+ if !ok {
+ return nil, spiceerrors.MustBugf("parameter %q not found in caveat %q", paramName, parentCaveatName)
+ }
+
+ decoded, err := caveattypes.DecodeParameterType(caveatTypeSet, paramType)
+ if err != nil {
+ return nil, spiceerrors.MustBugf("invalid parameter type on caveat: %v", err)
+ }
+
+ return &v1.ReflectionCaveatParameter{
+ Name: paramName,
+ Type: decoded.String(),
+ ParentCaveatName: parentCaveatName,
+ }, nil
+}