Skip to content

Commit 162f25b

Browse files
committed
feat: add ApplyPatch function for applying SCIM patch operations
Add a generic ApplyPatch function that applies a sequence of patch operations (add, replace, remove) to resource attributes, resolving attribute paths against the provided schema and extensions. This avoids every consumer having to independently implement the RFC 7644 Section 3.5.2 patch semantics, which is error-prone and duplicative.
1 parent 8304ef6 commit 162f25b

3 files changed

Lines changed: 942 additions & 0 deletions

File tree

examples_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,76 @@
11
package scim
22

33
import (
4+
"fmt"
45
logger "log"
56
"net/http"
7+
8+
"github.com/elimity-com/scim/schema"
9+
filter "github.com/scim2/filter-parser/v2"
610
)
711

12+
func ExampleApplyPatch() {
13+
// Define the schema for the resource.
14+
userSchema := schema.Schema{
15+
ID: schema.UserSchema,
16+
Attributes: schema.Attributes{
17+
schema.SimpleCoreAttribute(schema.SimpleStringParams(schema.StringParams{
18+
Name: "userName",
19+
})),
20+
schema.SimpleCoreAttribute(schema.SimpleStringParams(schema.StringParams{
21+
Name: "displayName",
22+
})),
23+
schema.ComplexCoreAttribute(schema.ComplexParams{
24+
Name: "emails",
25+
MultiValued: true,
26+
SubAttributes: []schema.SimpleParams{
27+
schema.SimpleStringParams(schema.StringParams{Name: "value"}),
28+
schema.SimpleStringParams(schema.StringParams{Name: "type"}),
29+
},
30+
}),
31+
},
32+
}
33+
34+
// The resource to patch, typically fetched from a data store.
35+
attrs := ResourceAttributes{
36+
"userName": "john",
37+
"displayName": "John Doe",
38+
"emails": []interface{}{
39+
map[string]interface{}{
40+
"value": "john@work.com",
41+
"type": "work",
42+
},
43+
},
44+
}
45+
46+
// Parse paths for the operations.
47+
emailsPath, _ := filter.ParsePath([]byte("emails"))
48+
workEmailValuePath, _ := filter.ParsePath([]byte(`emails[type eq "work"].value`))
49+
50+
// Apply a sequence of patch operations.
51+
result, err := ApplyPatch(attrs, []PatchOperation{
52+
{Op: PatchOperationAdd, Path: &emailsPath, Value: []interface{}{
53+
map[string]interface{}{"value": "john@home.com", "type": "home"},
54+
}},
55+
{Op: PatchOperationReplace, Path: &workEmailValuePath, Value: "john@newwork.com"},
56+
}, userSchema)
57+
if err != nil {
58+
panic(err)
59+
}
60+
61+
fmt.Println(result["userName"])
62+
emails := result["emails"].([]interface{})
63+
fmt.Println(len(emails))
64+
fmt.Println(emails[0].(map[string]interface{})["value"])
65+
fmt.Println(emails[1].(map[string]interface{})["value"])
66+
67+
// Output:
68+
// john
69+
// 2
70+
// john@newwork.com
71+
// john@home.com
72+
}
73+
874
func ExampleNewServer() {
975
args := &ServerArgs{
1076
ServiceProviderConfig: &ServiceProviderConfig{},

0 commit comments

Comments
 (0)