Custom store
Ce contenu n’est pas encore disponible dans votre langue.
All persistence in scimply goes through the store.ResourceStore interface. Implementing it lets you connect to any database, cache, or external API.
The interface
Section titled “The interface”type ResourceStore interface { Create(ctx context.Context, resourceType string, res *resource.Resource) (*resource.Resource, error) Get(ctx context.Context, resourceType string, id string) (*resource.Resource, error) List(ctx context.Context, resourceType string, params ListParams) (*ListResult, error) Replace(ctx context.Context, resourceType string, id string, res *resource.Resource) (*resource.Resource, error) Patch(ctx context.Context, resourceType string, id string, ops []resource.PatchOp) (*resource.Resource, error) Delete(ctx context.Context, resourceType string, id string) error}All implementations must be safe for concurrent use.
Sentinel errors
Section titled “Sentinel errors”Return these from your store so the server produces the correct HTTP status code:
| Error | HTTP status | When to return |
|---|---|---|
store.ErrNotFound | 404 | Resource does not exist |
store.ErrConflict | 409 | Uniqueness constraint violated (e.g. duplicate userName) |
Minimal example
Section titled “Minimal example”package mystore
import ( "context" "sync"
"github.com/cerberauth/scimply/resource" "github.com/cerberauth/scimply/store")
type MyStore struct { mu sync.RWMutex data map[string]map[string]*resource.Resource // resourceType → id → resource}
func New() *MyStore { return &MyStore{data: make(map[string]map[string]*resource.Resource)}}
func (s *MyStore) Create(ctx context.Context, resourceType string, res *resource.Resource) (*resource.Resource, error) { s.mu.Lock() defer s.mu.Unlock() if _, ok := s.data[resourceType]; !ok { s.data[resourceType] = make(map[string]*resource.Resource) } // TODO: generate ID, set meta fields, check uniqueness s.data[resourceType][res.ID] = res return res, nil}
func (s *MyStore) Get(ctx context.Context, resourceType, id string) (*resource.Resource, error) { s.mu.RLock() defer s.mu.RUnlock() if m, ok := s.data[resourceType]; ok { if r, ok := m[id]; ok { return r, nil } } return nil, store.ErrNotFound}
// ... implement remaining methodsDelegating filter evaluation
Section titled “Delegating filter evaluation”If your backend cannot translate params.Filter into a native query, set NeedsClientFilter: true in the returned ListResult. The server will apply the SCIM filter in-process using the built-in evaluator.
func (s *MyStore) List(ctx context.Context, resourceType string, params store.ListParams) (*store.ListResult, error) { // Fetch all resources without filtering all := s.fetchAll(resourceType) return &store.ListResult{ Resources: all, TotalResults: len(all), StartIndex: params.StartIndex, ItemsPerPage: len(all), NeedsClientFilter: true, // server will apply params.Filter }, nil}Registering the store
Section titled “Registering the store”Pass your store to server.New via WithStore:
srv, err := server.New( server.WithStore(mystore.New()), server.WithSchemaRegistry(reg),)