-
Notifications
You must be signed in to change notification settings - Fork 5
Attribute push action schema helper #626
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ import ( | |
| "errors" | ||
| "fmt" | ||
| "sort" | ||
| "strings" | ||
| "sync" | ||
| "time" | ||
|
|
||
|
|
@@ -14,6 +15,8 @@ import ( | |
| "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap/ctxzap" | ||
| "github.com/segmentio/ksuid" | ||
| "go.uber.org/zap" | ||
| "golang.org/x/text/cases" | ||
| "golang.org/x/text/language" | ||
| "google.golang.org/grpc/codes" | ||
| "google.golang.org/grpc/status" | ||
| "google.golang.org/protobuf/types/known/structpb" | ||
|
|
@@ -633,3 +636,69 @@ func isNullValue(v *structpb.Value) bool { | |
| _, isNull := v.GetKind().(*structpb.Value_NullValue) | ||
| return isNull | ||
| } | ||
|
|
||
| // toDisplayName converts a snake_case field name to a Title Case display name. | ||
| func toDisplayName(name string) string { | ||
| // Replace underscores with spaces, then apply title case | ||
| spaced := strings.ReplaceAll(name, "_", " ") | ||
| return cases.Title(language.English).String(spaced) | ||
| } | ||
|
|
||
| // NewUpdateProfileSchema creates a BatonActionSchema for updating user profiles. | ||
| // It follows the convention for ACTION_TYPE_ACCOUNT_UPDATE_PROFILE actions: | ||
| // - Named "update_profile" | ||
| // - Includes a "user_id" ResourceIdField | ||
| // - Includes StringField for each known attribute name | ||
| // - Optionally includes "custom_attributes" StringMapField if allowsCustom is true | ||
| func NewUpdateProfileSchema(allowsCustom bool, attributeNames []string) *v2.BatonActionSchema { | ||
| arguments := []*config.Field{ | ||
| config.Field_builder{ | ||
| Name: "user_id", | ||
| DisplayName: "User ID", | ||
| Description: "The user to update", | ||
| IsRequired: true, | ||
| ResourceIdField: &config.ResourceIdField{ | ||
| Rules: &config.ResourceIDRules{ | ||
| AllowedResourceTypeIds: []string{"user"}, | ||
| }, | ||
| }, | ||
| }.Build(), | ||
| } | ||
|
|
||
| // Dedupe attributeNames and remove reserved names | ||
| seen := make(map[string]bool) | ||
| reservedNames := map[string]bool{"user_id": true, "custom_attributes": true} | ||
| filteredNames := make([]string, 0, len(attributeNames)) | ||
| for _, name := range attributeNames { | ||
| if reservedNames[name] || seen[name] { | ||
| continue | ||
| } | ||
| seen[name] = true | ||
| filteredNames = append(filteredNames, name) | ||
| } | ||
|
|
||
| for _, name := range filteredNames { | ||
| arguments = append(arguments, config.Field_builder{ | ||
| Name: name, | ||
| DisplayName: toDisplayName(name), | ||
| StringField: &config.StringField{}, | ||
| }.Build()) | ||
| } | ||
|
|
||
| if allowsCustom { | ||
| arguments = append(arguments, config.Field_builder{ | ||
| Name: "custom_attributes", | ||
| DisplayName: "Custom Attributes", | ||
| Description: "Additional custom attributes to set on the user", | ||
| StringMapField: &config.StringMapField{}, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How are custom attributes supposed to be handled? eg: If I make an action and use {
"employee_id": "1234",
"custom_attributes": {
"employee_id": "5678"
}
}...what is supposed to happen? It's fine if the answer is, "Don't do that." Also how does this schema handle updating a subset of attributes? If it only adds/updates attributes that are provided (and doesn't delete the ones not present), then I don't think there's a way to delete an attribute. They can only be set to empty string.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ggreer as for what is supposed to happen, that's sorta connector dependent. For example, in the demo connector, if you provided an As of now, my intention was that connectors should only update the set of attributes they learn about, which does mean you can only empty-string those values. That felt like the safest path, but we still have the opportunity to revisit that. It's also not enforced in any way, just guidance. C1 sends every field for the user it can calculate a value for, even if it's the empty string (iirc), so again, a connector could decide to treat missing values as "delete-this-field" (whatever that means for that particular connector) |
||
| }.Build()) | ||
| } | ||
|
|
||
| return v2.BatonActionSchema_builder{ | ||
| Name: "update_profile", | ||
| DisplayName: "Update Profile", | ||
| Description: "Update a user's profile attributes", | ||
| ActionType: []v2.ActionType{v2.ActionType_ACTION_TYPE_ACCOUNT_UPDATE_PROFILE}, | ||
| Arguments: arguments, | ||
| }.Build() | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we have some connectors with resource type IDs other than "user" that have the user trait. If this action is registered on a specific resource type, wouldn't the input here always be a resource of that type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not necessarily, this is just a user_id field on the schema, not the resource type on the schema itself. These can be anything