Aller au contenu

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.

store/store.go
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.

Return these from your store so the server produces the correct HTTP status code:

ErrorHTTP statusWhen to return
store.ErrNotFound404Resource does not exist
store.ErrConflict409Uniqueness constraint violated (e.g. duplicate userName)
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 methods

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
}

Pass your store to server.New via WithStore:

srv, err := server.New(
server.WithStore(mystore.New()),
server.WithSchemaRegistry(reg),
)