Skip to content

Commit 3d5c10a

Browse files
committed
Add Support for User Attributes API to Schema.
1 parent 7850065 commit 3d5c10a

8 files changed

Lines changed: 442 additions & 53 deletions

pkg/api/api.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ type PermitApiClient struct {
129129
RoleAssignments *RoleAssignments
130130
Roles *Roles
131131
Tenants *Tenants
132+
UserAttributes *UserAttributes
132133
Users *Users
133134
}
134135

@@ -198,6 +199,7 @@ func NewPermitApiClient(config *config.PermitConfig) *PermitApiClient {
198199
RoleAssignments: NewRoleAssignmentsApi(client, config),
199200
Roles: NewRolesApi(client, config),
200201
Tenants: NewTenantsApi(client, config),
202+
UserAttributes: NewUserAttributesApi(client, config),
201203
Users: NewUsersApi(client, config),
202204
}
203205
}

pkg/api/userAttributes.go

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
package api
2+
3+
import (
4+
"context"
5+
6+
"github.com/google/uuid"
7+
"github.com/permitio/permit-golang/pkg/config"
8+
"github.com/permitio/permit-golang/pkg/errors"
9+
"github.com/permitio/permit-golang/pkg/models"
10+
"github.com/permitio/permit-golang/pkg/openapi"
11+
"go.uber.org/zap"
12+
)
13+
14+
// DefaultUserAttributeResourceID is the default resource_id query parameter for user schema attributes (V2 API).
15+
const DefaultUserAttributeResourceID = "__user"
16+
17+
type UserAttributes struct {
18+
permitBaseApi
19+
}
20+
21+
func NewUserAttributesApi(client *openapi.APIClient, config *config.PermitConfig) *UserAttributes {
22+
return &UserAttributes{
23+
permitBaseApi: permitBaseApi{
24+
client: client,
25+
config: config,
26+
logger: config.Logger,
27+
},
28+
}
29+
}
30+
31+
// List returns all user attributes for the User resource (resource_id __user).
32+
//
33+
// attrs, err := PermitClient.Api.UserAttributes.List(ctx, 1, 30)
34+
func (a *UserAttributes) List(ctx context.Context, page int, perPage int) ([]models.UserAttributeRead, error) {
35+
return a.ListForResource(ctx, DefaultUserAttributeResourceID, page, perPage)
36+
}
37+
38+
// ListForResource lists user attributes when using a non-default user resource id.
39+
func (a *UserAttributes) ListForResource(ctx context.Context, resourceID string, page int, perPage int) ([]models.UserAttributeRead, error) {
40+
perPageLimit := int32(DefaultPerPageLimit)
41+
if !isPaginationInLimit(int32(page), int32(perPage), perPageLimit) {
42+
err := errors.NewPermitPaginationError()
43+
a.logger.Error("error listing user attributes - max per page exceeded", zap.Error(err))
44+
return nil, err
45+
}
46+
err := a.lazyLoadPermitContext(ctx)
47+
if err != nil {
48+
return nil, err
49+
}
50+
attrs, _, err := a.client.UserAttributesApi.ListUserAttributes(ctx, a.config.Context.GetProject(), a.config.Context.GetEnvironment()).
51+
ResourceId(resourceID).Page(int32(page)).PerPage(int32(perPage)).Execute()
52+
if err != nil {
53+
a.logger.Error("error listing user attributes", zap.Error(err))
54+
return nil, err
55+
}
56+
return attrs, nil
57+
}
58+
59+
// Get returns a user attribute by id or key (slug).
60+
//
61+
// attr, err := PermitClient.Api.UserAttributes.Get(ctx, "clearance_level")
62+
func (a *UserAttributes) Get(ctx context.Context, attributeIDOrKey string) (*models.UserAttributeRead, error) {
63+
return a.GetForResource(ctx, DefaultUserAttributeResourceID, attributeIDOrKey)
64+
}
65+
66+
// GetForResource gets a user attribute with an explicit resource_id query.
67+
func (a *UserAttributes) GetForResource(ctx context.Context, resourceID, attributeIDOrKey string) (*models.UserAttributeRead, error) {
68+
err := a.lazyLoadPermitContext(ctx)
69+
if err != nil {
70+
return nil, err
71+
}
72+
attr, _, err := a.client.UserAttributesApi.GetUserAttribute(ctx, a.config.Context.GetProject(), a.config.Context.GetEnvironment(), attributeIDOrKey).
73+
ResourceId(resourceID).Execute()
74+
if err != nil {
75+
a.logger.Error("error getting user attribute: "+attributeIDOrKey, zap.Error(err))
76+
return nil, err
77+
}
78+
return attr, nil
79+
}
80+
81+
// GetByKey is an alias for Get (attribute key is the slug).
82+
func (a *UserAttributes) GetByKey(ctx context.Context, attributeKey string) (*models.UserAttributeRead, error) {
83+
return a.Get(ctx, attributeKey)
84+
}
85+
86+
// GetById gets a user attribute by attribute UUID (uses default user resource __user).
87+
func (a *UserAttributes) GetById(ctx context.Context, attributeID uuid.UUID) (*models.UserAttributeRead, error) {
88+
return a.Get(ctx, attributeID.String())
89+
}
90+
91+
// Create adds a user schema attribute.
92+
//
93+
// create := models.NewUserAttributeCreate("department", models.STRING)
94+
// create.SetDescription("User department")
95+
// attr, err := PermitClient.Api.UserAttributes.Create(ctx, *create)
96+
func (a *UserAttributes) Create(ctx context.Context, body models.UserAttributeCreate) (*models.UserAttributeRead, error) {
97+
return a.CreateForResource(ctx, DefaultUserAttributeResourceID, body)
98+
}
99+
100+
// CreateForResource creates a user attribute with an explicit resource_id query.
101+
func (a *UserAttributes) CreateForResource(ctx context.Context, resourceID string, body models.UserAttributeCreate) (*models.UserAttributeRead, error) {
102+
err := a.lazyLoadPermitContext(ctx)
103+
if err != nil {
104+
return nil, err
105+
}
106+
attr, _, err := a.client.UserAttributesApi.CreateUserAttribute(ctx, a.config.Context.GetProject(), a.config.Context.GetEnvironment()).
107+
UserAttributeCreate(body).ResourceId(resourceID).Execute()
108+
if err != nil {
109+
a.logger.Error("error creating user attribute: "+body.GetKey(), zap.Error(err))
110+
return nil, err
111+
}
112+
return attr, nil
113+
}
114+
115+
// Update partially updates a user attribute (id or key).
116+
func (a *UserAttributes) Update(ctx context.Context, attributeIDOrKey string, body models.UserAttributeUpdate) (*models.UserAttributeRead, error) {
117+
return a.UpdateForResource(ctx, DefaultUserAttributeResourceID, attributeIDOrKey, body)
118+
}
119+
120+
// UpdateForResource updates with an explicit resource_id query.
121+
func (a *UserAttributes) UpdateForResource(ctx context.Context, resourceID, attributeIDOrKey string, body models.UserAttributeUpdate) (*models.UserAttributeRead, error) {
122+
err := a.lazyLoadPermitContext(ctx)
123+
if err != nil {
124+
return nil, err
125+
}
126+
attr, _, err := a.client.UserAttributesApi.UpdateUserAttribute(ctx, a.config.Context.GetProject(), a.config.Context.GetEnvironment(), attributeIDOrKey).
127+
UserAttributeUpdate(body).ResourceId(resourceID).Execute()
128+
if err != nil {
129+
a.logger.Error("error updating user attribute: "+attributeIDOrKey, zap.Error(err))
130+
return nil, err
131+
}
132+
return attr, nil
133+
}
134+
135+
// Delete removes a user attribute (id or key).
136+
func (a *UserAttributes) Delete(ctx context.Context, attributeIDOrKey string) error {
137+
return a.DeleteForResource(ctx, DefaultUserAttributeResourceID, attributeIDOrKey)
138+
}
139+
140+
// DeleteForResource deletes with an explicit resource_id query.
141+
func (a *UserAttributes) DeleteForResource(ctx context.Context, resourceID, attributeIDOrKey string) error {
142+
err := a.lazyLoadPermitContext(ctx)
143+
if err != nil {
144+
return err
145+
}
146+
_, err = a.client.UserAttributesApi.DeleteUserAttribute(ctx, a.config.Context.GetProject(), a.config.Context.GetEnvironment(), attributeIDOrKey).
147+
ResourceId(resourceID).Execute()
148+
if err != nil {
149+
a.logger.Error("error deleting user attribute: "+attributeIDOrKey, zap.Error(err))
150+
return err
151+
}
152+
return nil
153+
}

pkg/models/model_attribute_type.go

Lines changed: 16 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package models
2+
3+
import "encoding/json"
4+
5+
// UserAttributeCreate is the request body for creating a user (schema) attribute.
6+
// It matches the V2 POST /v2/schema/{proj_id}/{env_id}/users/attributes payload.
7+
type UserAttributeCreate struct {
8+
// A URL-friendly name of the attribute (i.e: slug).
9+
Key string `json:"key"`
10+
// The type of the attribute (bool, number, string, time, array, json, object, object_array).
11+
Type AttributeType `json:"type"`
12+
// An optional longer description of what this attribute represents in your system.
13+
Description *string `json:"description,omitempty"`
14+
}
15+
16+
// NewUserAttributeCreate builds a create request with required key and type.
17+
func NewUserAttributeCreate(key string, type_ AttributeType) *UserAttributeCreate {
18+
return &UserAttributeCreate{Key: key, Type: type_}
19+
}
20+
21+
// NewUserAttributeCreateWithDefaults instantiates an empty UserAttributeCreate.
22+
func NewUserAttributeCreateWithDefaults() *UserAttributeCreate {
23+
return &UserAttributeCreate{}
24+
}
25+
26+
func (o *UserAttributeCreate) GetKey() string {
27+
if o == nil {
28+
return ""
29+
}
30+
return o.Key
31+
}
32+
33+
func (o *UserAttributeCreate) SetKey(v string) {
34+
o.Key = v
35+
}
36+
37+
func (o *UserAttributeCreate) GetType() AttributeType {
38+
if o == nil {
39+
return ""
40+
}
41+
return o.Type
42+
}
43+
44+
func (o *UserAttributeCreate) SetType(v AttributeType) {
45+
o.Type = v
46+
}
47+
48+
func (o *UserAttributeCreate) GetDescription() string {
49+
if o == nil || IsNil(o.Description) {
50+
return ""
51+
}
52+
return *o.Description
53+
}
54+
55+
func (o *UserAttributeCreate) SetDescription(v string) {
56+
o.Description = &v
57+
}
58+
59+
func (o UserAttributeCreate) MarshalJSON() ([]byte, error) {
60+
m := map[string]interface{}{"key": o.Key, "type": o.Type}
61+
if !IsNil(o.Description) {
62+
m["description"] = o.Description
63+
}
64+
return json.Marshal(m)
65+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package models
2+
3+
import (
4+
"encoding/json"
5+
"time"
6+
)
7+
8+
// UserAttributeRead is a user schema attribute as returned by the V2 API.
9+
type UserAttributeRead struct {
10+
Type AttributeType `json:"type"`
11+
Description *string `json:"description,omitempty"`
12+
Key string `json:"key"`
13+
Id string `json:"id"`
14+
ResourceId string `json:"resource_id"`
15+
ResourceKey string `json:"resource_key"`
16+
OrganizationId string `json:"organization_id"`
17+
ProjectId string `json:"project_id"`
18+
EnvironmentId string `json:"environment_id"`
19+
CreatedAt time.Time `json:"created_at"`
20+
UpdatedAt time.Time `json:"updated_at"`
21+
BuiltIn bool `json:"built_in"`
22+
}
23+
24+
func NewUserAttributeReadWithDefaults() *UserAttributeRead {
25+
return &UserAttributeRead{}
26+
}
27+
28+
func (o *UserAttributeRead) GetKey() string {
29+
if o == nil {
30+
return ""
31+
}
32+
return o.Key
33+
}
34+
35+
func (o *UserAttributeRead) GetId() string {
36+
if o == nil {
37+
return ""
38+
}
39+
return o.Id
40+
}
41+
42+
func (o *UserAttributeRead) GetType() AttributeType {
43+
if o == nil {
44+
return ""
45+
}
46+
return o.Type
47+
}
48+
49+
func (o *UserAttributeRead) GetBuiltIn() bool {
50+
if o == nil {
51+
return false
52+
}
53+
return o.BuiltIn
54+
}
55+
56+
func (o *UserAttributeRead) GetDescription() string {
57+
if o == nil || IsNil(o.Description) {
58+
return ""
59+
}
60+
return *o.Description
61+
}
62+
63+
func (o UserAttributeRead) MarshalJSON() ([]byte, error) {
64+
m := map[string]interface{}{
65+
"type": o.Type, "key": o.Key, "id": o.Id,
66+
"resource_id": o.ResourceId, "resource_key": o.ResourceKey,
67+
"organization_id": o.OrganizationId, "project_id": o.ProjectId,
68+
"environment_id": o.EnvironmentId, "created_at": o.CreatedAt,
69+
"updated_at": o.UpdatedAt, "built_in": o.BuiltIn,
70+
}
71+
if !IsNil(o.Description) {
72+
m["description"] = o.Description
73+
}
74+
return json.Marshal(m)
75+
}

0 commit comments

Comments
 (0)