Skip to content

Commit

Permalink
add handlers for resource types and schemas
Browse files Browse the repository at this point in the history
  • Loading branch information
imulab committed Feb 1, 2020
1 parent 670652b commit 8ed9b4e
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 8 deletions.
6 changes: 6 additions & 0 deletions cmd/api/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@ func Command() *cli.Command {
app := args.Initialize()
defer app.Close()

app.ensureSchemaRegistered()

var router = httprouter.New()
{
router.GET("/ServiceProviderConfig", ServiceProviderConfigHandler(app.ServiceProviderConfig()))
router.GET("/Schemas", SchemasHandler())
router.GET("/Schemas/:id", SchemaByIdHandler())
router.GET("/ResourceTypes", ResourceTypesHandler(app.UserResourceType(), app.GroupResourceType()))
router.GET("/ResourceTypes/:id", ResourceTypeByIdHandler(app.userResourceType, app.GroupResourceType()))

router.GET("/Users/:id", GetHandler(app.UserGetService(), app.Logger()))
router.GET("/Users", SearchHandler(app.UserQueryService(), app.Logger()))
Expand Down
110 changes: 110 additions & 0 deletions cmd/api/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/readpref"
"net/http"
"net/http/httptest"
)

// CreateHandler returns a route handler function for creating SCIM resources.
Expand Down Expand Up @@ -247,6 +248,115 @@ func ServiceProviderConfigHandler(config *spec.ServiceProviderConfig) func(rw ht
}
}

// ResourceTypesHandler returns a route handler function for getting all defined ResourceType.
func ResourceTypesHandler(resourceTypes ...*spec.ResourceType) func(rw http.ResponseWriter, r *http.Request, params httprouter.Params) {
result := &service.QueryResponse{
TotalResults: len(resourceTypes),
StartIndex: 1,
ItemsPerPage: len(resourceTypes),
Resources: []json.Serializable{},
}
for _, resourceType := range resourceTypes {
result.Resources = append(result.Resources, json.ResourceTypeToSerializable(resourceType))
}

// use recorder to cache render result
recorder := httptest.NewRecorder()
if err := handlerutil.WriteSearchResultToResponse(recorder, result); err != nil {
panic(err)
}

return func(rw http.ResponseWriter, r *http.Request, params httprouter.Params) {
rw.WriteHeader(200)
rw.Header().Set("Content-Type", recorder.Header().Get("Content-Type"))
_, _ = rw.Write(recorder.Body.Bytes())
}
}

// ResourceTypeByIdHandler returns a route handler function get ResourceType by its id.
func ResourceTypeByIdHandler(resourceTypes ...*spec.ResourceType) func(rw http.ResponseWriter, r *http.Request, params httprouter.Params) {
cache := map[string]gojson.RawMessage{}
for _, resourceType := range resourceTypes {
raw, err := json.Serialize(json.ResourceTypeToSerializable(resourceType))
if err != nil {
panic(err)
}
cache[resourceType.ID()] = raw
}

return func(rw http.ResponseWriter, r *http.Request, params httprouter.Params) {
raw, ok := cache[params.ByName("id")]
if !ok {
_ = handlerutil.WriteError(rw, fmt.Errorf("%w: resource type is not found", spec.ErrNotFound))
return
}

rw.WriteHeader(200)
rw.Header().Set("Content-Type", "application/json+scim")
_, _ = rw.Write(raw)
}
}

// SchemasHandler returns a route handler function for getting all defined Schema.
func SchemasHandler() func(rw http.ResponseWriter, r *http.Request, params httprouter.Params) {
result := &service.QueryResponse{StartIndex: 1, Resources: []json.Serializable{}}
if err := spec.Schemas().ForEachSchema(func(schema *spec.Schema) error {
if schema.ID() == spec.CoreSchemaId {
return nil
}
result.Resources = append(result.Resources, json.SchemaToSerializable(schema))
return nil
}); err != nil {
panic(err)
}
result.TotalResults = len(result.Resources)
result.ItemsPerPage = len(result.Resources)

// use recorder to cache render result
recorder := httptest.NewRecorder()
if err := handlerutil.WriteSearchResultToResponse(recorder, result); err != nil {
panic(err)
}

return func(rw http.ResponseWriter, r *http.Request, params httprouter.Params) {
rw.WriteHeader(200)
rw.Header().Set("Content-Type", recorder.Header().Get("Content-Type"))
_, _ = rw.Write(recorder.Body.Bytes())
}
}

// SchemaByIdHandler returns a route handler function get Schema by its id.
func SchemaByIdHandler() func(rw http.ResponseWriter, r *http.Request, params httprouter.Params) {
cache := map[string]gojson.RawMessage{}
if err := spec.Schemas().ForEachSchema(func(schema *spec.Schema) error {
if schema.ID() == spec.CoreSchemaId {
return nil
}

raw, err := json.Serialize(json.SchemaToSerializable(schema))
if err != nil {
return err
}
cache[schema.ID()] = raw

return nil
}); err != nil {
panic(err)
}

return func(rw http.ResponseWriter, r *http.Request, params httprouter.Params) {
raw, ok := cache[params.ByName("id")]
if !ok {
_ = handlerutil.WriteError(rw, fmt.Errorf("%w: schema is not found", spec.ErrNotFound))
return
}

rw.WriteHeader(200)
rw.Header().Set("Content-Type", "application/json+scim")
_, _ = rw.Write(raw)
}
}

// HealthHandler returns a http handler to report service health status.
func HealthHandler(mongoClient *mongo.Client, rabbitConn *amqp.Connection) func(rw http.ResponseWriter, r *http.Request, params httprouter.Params) {
return func(rw http.ResponseWriter, r *http.Request, params httprouter.Params) {
Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@ require (
github.com/imulab/go-scim/mongo/v2 v2.0.0
github.com/imulab/go-scim/pkg/v2 v2.0.0
github.com/julienschmidt/httprouter v1.3.0
github.com/opencontainers/runc v1.0.0-rc9 // indirect
github.com/ory/dockertest v3.3.5+incompatible
github.com/rs/zerolog v1.17.2
github.com/satori/go.uuid v1.2.0
github.com/streadway/amqp v0.0.0-20200108173154-1c71cc93ed71
github.com/stretchr/objx v0.2.0 // indirect
github.com/stretchr/testify v1.4.0
github.com/urfave/cli/v2 v2.1.1
go.mongodb.org/mongo-driver v1.2.1
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
golang.org/x/sys v0.0.0-20200121082415-34d275377bf9 // indirect
gopkg.in/yaml.v2 v2.2.7 // indirect
)

replace github.com/imulab/go-scim/mongo/v2 => ./mongo/v2
Expand Down
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVo
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v1.0.0-rc5 h1:rYjdzMDXVly2Av0RLs3nf/iVkaWh2UrDhuTdTT2KggQ=
github.com/opencontainers/runc v1.0.0-rc5/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/runc v1.0.0-rc9 h1:/k06BMULKF5hidyoZymkoDCzdJzltZpz/UU4LguQVtc=
github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA=
github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
Expand All @@ -69,7 +71,10 @@ github.com/streadway/amqp v0.0.0-20200108173154-1c71cc93ed71/go.mod h1:AZpEONHx3
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
Expand Down Expand Up @@ -103,6 +108,8 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200121082415-34d275377bf9 h1:N19i1HjUnR7TF7rMt8O4p3dLvqvmYyzB6ifMFmrbY50=
golang.org/x/sys v0.0.0-20200121082415-34d275377bf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
Expand All @@ -116,3 +123,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
10 changes: 7 additions & 3 deletions pkg/v2/service/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"github.com/imulab/go-scim/pkg/v2/crud"
"github.com/imulab/go-scim/pkg/v2/crud/expr"
"github.com/imulab/go-scim/pkg/v2/db"
"github.com/imulab/go-scim/pkg/v2/prop"
"github.com/imulab/go-scim/pkg/v2/json"
"github.com/imulab/go-scim/pkg/v2/spec"
)

Expand Down Expand Up @@ -36,7 +36,7 @@ type (
TotalResults int
StartIndex int
ItemsPerPage int
Resources []*prop.Resource
Resources []json.Serializable
Projection *crud.Projection // included so that caller may render properly
}
)
Expand Down Expand Up @@ -77,9 +77,13 @@ func (s *queryService) Do(ctx context.Context, req *QueryRequest) (resp *QueryRe
}
}

if resp.Resources, err = s.database.Query(ctx, req.Filter, req.Sort, req.Pagination, req.Projection); err != nil {
resources, err := s.database.Query(ctx, req.Filter, req.Sort, req.Pagination, req.Projection)
if err != nil {
return
}
for _, r := range resources {
resp.Resources = append(resp.Resources, r)
}

resp.ItemsPerPage = len(resp.Resources)
return
Expand Down
6 changes: 3 additions & 3 deletions pkg/v2/service/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (s *QueryServiceTestSuite) TestDo() {
assert.Nil(t, err)
assert.Equal(t, 1, resp.TotalResults)
assert.Len(t, resp.Resources, 1)
assert.Equal(t, "user003", resp.Resources[0].Navigator().Dot("id").Current().Raw())
assert.Equal(t, "user003", resp.Resources[0].(*prop.Resource).Navigator().Dot("id").Current().Raw())
},
},
{
Expand Down Expand Up @@ -119,7 +119,7 @@ func (s *QueryServiceTestSuite) TestDo() {
assert.Equal(t, 5, resp.TotalResults)
assert.Len(t, resp.Resources, 5)
for i, expected := range []string{"user005", "user004", "user003", "user002", "user001"} {
assert.Equal(t, expected, resp.Resources[i].Navigator().Dot("id").Current().Raw())
assert.Equal(t, expected, resp.Resources[i].(*prop.Resource).Navigator().Dot("id").Current().Raw())
}
},
},
Expand Down Expand Up @@ -156,7 +156,7 @@ func (s *QueryServiceTestSuite) TestDo() {
assert.Equal(t, 5, resp.TotalResults)
assert.Len(t, resp.Resources, 2)
for i, expected := range []string{"user002", "user003"} {
assert.Equal(t, expected, resp.Resources[i].Navigator().Dot("id").Current().Raw())
assert.Equal(t, expected, resp.Resources[i].(*prop.Resource).Navigator().Dot("id").Current().Raw())
}
},
},
Expand Down
7 changes: 5 additions & 2 deletions pkg/v2/spec/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,13 @@ func (r *schemaRegistry) Get(schemaId string) (schema *Schema, ok bool) {
}

// ForEachSchema invokes the callback function on each registered schema.
func (r *schemaRegistry) ForEachSchema(callback func(schema *Schema)) {
func (r *schemaRegistry) ForEachSchema(callback func(schema *Schema) error) error {
for _, schema := range r.db {
callback(schema)
if err := callback(schema); err != nil {
return err
}
}
return nil
}

func (r *schemaRegistry) mustGet(schemaId string) *Schema {
Expand Down

0 comments on commit 8ed9b4e

Please sign in to comment.