Skip to content

Commit

Permalink
Merge pull request #48 from elimity-com/feature/47-errors-package
Browse files Browse the repository at this point in the history
errors package
  • Loading branch information
jdeflander authored May 21, 2019
2 parents 522d50c + c2d2953 commit 91bab90
Show file tree
Hide file tree
Showing 15 changed files with 250 additions and 202 deletions.
107 changes: 34 additions & 73 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"fmt"
"net/http"
"strconv"

"github.com/elimity-com/scim/errors"
)

type scimType string
Expand Down Expand Up @@ -107,83 +109,42 @@ func (e *scimError) UnmarshalJSON(data []byte) error {
return nil
}

// GetError represents an error that is returned by a GET HTTP request.
type GetError struct {
getErr scimError
}

// GetErrorNil indicates that no error occurred during handling a GET HTTP request.
var GetErrorNil GetError

var (
// GetErrorInvalidValue shall be returned when a required field is missing or a value is not compatible with the
// attribute type.
GetErrorInvalidValue = GetError{getErr: scimErrorInvalidValue}
)

// NewResourceNotFoundGetError returns an error with status code 404 and a human readable message containing the identifier
// of the resource that was requested but not found.
func NewResourceNotFoundGetError(id string) GetError {
return GetError{scimErrorResourceNotFound(id)}
}

// PostError represents an error that is returned by a POST HTTP request.
type PostError struct {
postErr scimError
}

// PostErrorNil indicates that no error occurred during handling a POST HTTP request.
var PostErrorNil PostError

var (
// PostErrorUniqueness shall be returned when one or more of the attribute values are already in use or are reserved.
PostErrorUniqueness = PostError{postErr: scimErrorUniqueness}
// PostErrorInvalidSyntax shall be returned when the request body message structure was invalid or did not conform
// to the request schema.
PostErrorInvalidSyntax = PostError{postErr: scimErrorInvalidSyntax}
// PostErrorInvalidValue shall be returned when a required field is missing or a value is not compatible with the
// attribute type.
PostErrorInvalidValue = PostError{postErr: scimErrorInvalidValue}
)

// PutError represents an error that is returned by a PUT HTTP request.
type PutError struct {
putErr scimError
func scimGetError(getError errors.GetError, id string) scimError {
switch getError {
case errors.GetErrorResourceNotFound:
return scimErrorResourceNotFound(id)
default:
return scimErrorInternalServer
}
}

// PutErrorNil indicates that no error occurred during handling a PUT HTTP request.
var PutErrorNil PutError

var (
// PutErrorUniqueness shall be returned when one or more of the attribute values are already in use or are reserved.
PutErrorUniqueness = PutError{putErr: scimErrorUniqueness}
// PutErrorMutability shall be returned when the attempted modification is not compatible with the target
// attribute's mutability or current state.
PutErrorMutability = PutError{putErr: scimErrorMutability}
// PutErrorInvalidSyntax shall be returned when the request body message structure was invalid or did not conform
// to the request schema.
PutErrorInvalidSyntax = PutError{putErr: scimErrorInvalidSyntax}
// PutErrorInvalidValue shall be returned when a required field is missing or a value is not compatible with the
// attribute type.
PutErrorInvalidValue = PutError{putErr: scimErrorInvalidValue}
)

// NewResourceNotFoundPutError returns an error with status code 404 and a human readable message containing the identifier
// of the resource that was requested to be replaced but not found.
func NewResourceNotFoundPutError(id string) PutError {
return PutError{scimErrorResourceNotFound(id)}
func scimPostError(postError errors.PostError) scimError {
switch postError {
case errors.PostErrorUniqueness:
return scimErrorUniqueness
default:
return scimErrorInternalServer
}
}

// DeleteError represents an error that is returned by a DELETE HTTP request.
type DeleteError struct {
delErr scimError
func scimPutError(putError errors.PutError, id string) scimError {
switch putError {
case errors.PutErrorUniqueness:
return scimErrorUniqueness
case errors.PutErrorMutability:
return scimErrorMutability
case errors.PutErrorResourceNotFound:
return scimErrorResourceNotFound(id)
default:
return scimErrorInternalServer
}
}

// DeleteErrorNil indicates that no error occurred during handling a DELETE HTTP request.
var DeleteErrorNil DeleteError

// NewResourceNotFoundDeleteError returns an error with status code 404 and a human readable message containing the identifier
// of the resource that was requested to be deleted but not found.
func NewResourceNotFoundDeleteError(id string) DeleteError {
return DeleteError{scimErrorResourceNotFound(id)}
func scimDeleteError(deleteError errors.DeleteError, id string) scimError {
switch deleteError {
case errors.DeleteErrorResourceNotFound:
return scimErrorResourceNotFound(id)
default:
return scimErrorInternalServer
}
}
49 changes: 49 additions & 0 deletions errors/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package errors

// GetError represents an error that is returned by a GET HTTP request.
type GetError int

const (
// GetErrorNil indicates that no error occurred during handling a GET HTTP request.
GetErrorNil GetError = iota
// GetErrorResourceNotFound returns an error with status code 404 and a human readable message containing the identifier
// of the resource that was requested but not found.
GetErrorResourceNotFound
)

// PostError represents an error that is returned by a POST HTTP request.
type PostError int

const (
// PostErrorNil indicates that no error occurred during handling a POST HTTP request.
PostErrorNil PostError = iota
// PostErrorUniqueness shall be returned when one or more of the attribute values are already in use or are reserved.
PostErrorUniqueness
)

// PutError represents an error that is returned by a PUT HTTP request.
type PutError int

const (
// PutErrorNil indicates that no error occurred during handling a PUT HTTP request.
PutErrorNil PutError = iota
// PutErrorUniqueness shall be returned when one or more of the attribute values are already in use or are reserved.
PutErrorUniqueness
// PutErrorMutability shall be returned when the attempted modification is not compatible with the target
// attribute's mutability or current state.
PutErrorMutability
// PutErrorResourceNotFound returns an error with status code 404 and a human readable message containing the identifier
// of the resource that was requested to be replaced but not found.
PutErrorResourceNotFound
)

// DeleteError represents an error that is returned by a DELETE HTTP request.
type DeleteError int

const (
// DeleteErrorNil indicates that no error occurred during handling a DELETE HTTP request.
DeleteErrorNil DeleteError = iota
// DeleteErrorResourceNotFound returns an error with status code 404 and a human readable message containing the identifier
// of the resource that was requested to be deleted but not found.
DeleteErrorResourceNotFound
)
23 changes: 15 additions & 8 deletions examples_test.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
package scim

import (
"io/ioutil"
"log"
"net/http"
)

// Errors are ignored to keep it simple.
func ExampleNewServer() {
config, _ := NewServiceProviderConfigFromFile("/path/to/config")
schema, _ := NewSchemaFromFile("/path/to/schema")
resourceType, _ := NewResourceTypeFromFile("/path/to/resourceType", nil)
server, _ := NewServer(config, []Schema{schema}, []ResourceType{resourceType})
rawConfig, _ := ioutil.ReadFile("/path/to/config")
config, _ := NewServiceProviderConfig(rawConfig)
rawSchema, _ := ioutil.ReadFile("/path/to/schema")
schema, _ := NewSchema(rawSchema)
rawResourceType, _ := ioutil.ReadFile("/path/to/resourceType")
resourceType, _ := NewResourceType(rawResourceType, nil)

server, _ := NewServer(config, []Schema{schema}, []ResourceType{resourceType})
log.Fatal(http.ListenAndServe(":8080", server))
}

// Errors are ignored to keep it simple.
func ExampleNewServer_basePath() {
config, _ := NewServiceProviderConfigFromFile("/path/to/config")
schema, _ := NewSchemaFromFile("/path/to/schema")
resourceType, _ := NewResourceTypeFromFile("/path/to/resourceType", nil)
server, _ := NewServer(config, []Schema{schema}, []ResourceType{resourceType})
rawConfig, _ := ioutil.ReadFile("/path/to/config")
config, _ := NewServiceProviderConfig(rawConfig)
rawSchema, _ := ioutil.ReadFile("/path/to/schema")
schema, _ := NewSchema(rawSchema)
rawResourceType, _ := ioutil.ReadFile("/path/to/resourceType")
resourceType, _ := NewResourceType(rawResourceType, nil)

server, _ := NewServer(config, []Schema{schema}, []ResourceType{resourceType})
http.Handle("/scim/", http.StripPrefix("/scim", server))
log.Fatal(http.ListenAndServe(":8080", nil))
}
26 changes: 11 additions & 15 deletions handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"io/ioutil"
"log"
"net/http"

"github.com/elimity-com/scim/errors"
)

func errorHandler(w http.ResponseWriter, r *http.Request, scimErr scimError) {
Expand Down Expand Up @@ -131,8 +133,8 @@ func (s Server) resourcePostHandler(w http.ResponseWriter, r *http.Request, reso
}

resource, postErr := resourceType.handler.Create(attributes)
if postErr != PostErrorNil {
errorHandler(w, r, postErr.postErr)
if postErr != errors.PostErrorNil {
errorHandler(w, r, scimPostError(postErr))
return
}

Expand All @@ -153,8 +155,8 @@ func (s Server) resourcePostHandler(w http.ResponseWriter, r *http.Request, reso
// RFC: https://tools.ietf.org/html/rfc7644#section-3.4
func (s Server) resourceGetHandler(w http.ResponseWriter, r *http.Request, id string, resourceType resourceType) {
resource, getErr := resourceType.handler.Get(id)
if getErr != GetErrorNil {
errorHandler(w, r, getErr.getErr)
if getErr != errors.GetErrorNil {
errorHandler(w, r, scimGetError(getErr, id))
return
}

Expand All @@ -173,14 +175,8 @@ func (s Server) resourceGetHandler(w http.ResponseWriter, r *http.Request, id st
// resourcesGetHandler receives an HTTP GET request to the resource endpoint, e.g., "/Users" or "/Groups", to retrieve
// all known resources.
func (s Server) resourcesGetHandler(w http.ResponseWriter, r *http.Request, resourceType resourceType) {
res, getErr := resourceType.handler.GetAll()
if getErr != GetErrorNil {
errorHandler(w, r, getErr.getErr)
return
}

var resources []interface{}
for _, resource := range res {
for _, resource := range resourceType.handler.GetAll() {
resources = append(resources, resource.response(resourceType))
}

Expand Down Expand Up @@ -210,8 +206,8 @@ func (s Server) resourcePutHandler(w http.ResponseWriter, r *http.Request, id st
}

resource, putError := resourceType.handler.Replace(id, attributes)
if putError != PutErrorNil {
errorHandler(w, r, putError.putErr)
if putError != errors.PutErrorNil {
errorHandler(w, r, scimPutError(putError, id))
return
}

Expand All @@ -231,8 +227,8 @@ func (s Server) resourcePutHandler(w http.ResponseWriter, r *http.Request, id st
// RFC: https://tools.ietf.org/html/rfc7644#section-3.6
func (s Server) resourceDeleteHandler(w http.ResponseWriter, r *http.Request, id string, resourceType resourceType) {
deleteErr := resourceType.handler.Delete(id)
if deleteErr != DeleteErrorNil {
errorHandler(w, r, deleteErr.delErr)
if deleteErr != errors.DeleteErrorNil {
errorHandler(w, r, scimDeleteError(deleteErr, id))
return
}
w.WriteHeader(http.StatusNoContent)
Expand Down
39 changes: 35 additions & 4 deletions handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,46 @@ package scim

import (
"encoding/json"
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
"testing"
)

func newTestServer() (*Server, error) {
config, err := NewServiceProviderConfigFromFile("testdata/simple_service_provider_config.json")
rawConfig, err := ioutil.ReadFile("testdata/simple_service_provider_config.json")
if err != nil {
return nil, err
}
userSchema, err := NewSchemaFromFile("testdata/simple_user_schema.json")
config, err := NewServiceProviderConfig(rawConfig)
if err != nil {
return nil, err
}
userSchemaExtension, err := NewSchemaFromFile("testdata/simple_user_extension_schema.json")

rawSchema, err := ioutil.ReadFile("testdata/simple_user_schema.json")
if err != nil {
return nil, err
}
userSchema, err := NewSchema(rawSchema)
if err != nil {
return nil, err
}

rawExtension, err := ioutil.ReadFile("testdata/simple_user_extension_schema.json")
if err != nil {
return nil, err
}
userSchemaExtension, err := NewSchema(rawExtension)
if err != nil {
return nil, err
}

rawResourceType, err := ioutil.ReadFile("testdata/simple_user_resource_type_with_extension.json")
if err != nil {
return nil, err
}
userResourceType, err := NewResourceTypeFromFile("testdata/simple_user_resource_type_with_extension.json", newTestResourceHandler())
userResourceType, err := NewResourceType(rawResourceType, newTestResourceHandler())
if err != nil {
return nil, err
}
Expand All @@ -33,6 +53,17 @@ func newTestServer() (*Server, error) {
return &server, err
}

func newTestResourceHandler() testResourceHandler {
data := make(map[string]ResourceAttributes)
data["0001"] = ResourceAttributes{
"userName": "test",
}

return testResourceHandler{
data: data,
}
}

func TestErr(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/Invalid", nil)
rr := httptest.NewRecorder()
Expand Down
Loading

0 comments on commit 91bab90

Please sign in to comment.