Add service endpoints.
This commit is contained in:
committed by
Ralf Haferkamp
parent
9b8adb65ed
commit
20f6a212f3
@@ -60,6 +60,13 @@ type EducationBackend interface {
|
|||||||
// RemoveUserFromEducationSchool removes a single member (by ID) from a school
|
// RemoveUserFromEducationSchool removes a single member (by ID) from a school
|
||||||
RemoveUserFromEducationSchool(ctx context.Context, schoolID string, memberID string) error
|
RemoveUserFromEducationSchool(ctx context.Context, schoolID string, memberID string) error
|
||||||
|
|
||||||
|
// GetEducationSchoolClasses lists all classes in a chool
|
||||||
|
GetEducationSchoolClasses(ctx context.Context, schoolNumberOrID string) ([]*libregraph.EducationClass, error)
|
||||||
|
// AddClassesToEducationSchool adds new classes (referenced by a slice of IDs) to supplied school in the identity backend.
|
||||||
|
AddClassesToEducationSchool(ctx context.Context, schoolNumberOrID string, memberIDs []string) error
|
||||||
|
// RemoveClassFromEducationSchool removes a class from a school.
|
||||||
|
RemoveClassFromEducationSchool(ctx context.Context, schoolNumberOrID string, memberID string) error
|
||||||
|
|
||||||
// GetEducationClasses lists all classes
|
// GetEducationClasses lists all classes
|
||||||
GetEducationClasses(ctx context.Context, queryParam url.Values) ([]*libregraph.EducationClass, error)
|
GetEducationClasses(ctx context.Context, queryParam url.Values) ([]*libregraph.EducationClass, error)
|
||||||
// GetEducationClasses reads a given class by id
|
// GetEducationClasses reads a given class by id
|
||||||
|
|||||||
@@ -40,6 +40,21 @@ func (i *ErrEducationBackend) GetEducationSchoolUsers(ctx context.Context, id st
|
|||||||
return nil, errNotImplemented
|
return nil, errNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetEducationSchoolClasses implements the EducationBackend interface for the ErrEducationBackend backend.
|
||||||
|
func (i *ErrEducationBackend) GetEducationSchoolClasses(ctx context.Context, schoolNumberOrID string) ([]*libregraph.EducationClass, error) {
|
||||||
|
return nil, errNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddClassesToEducationSchool implements the EducationBackend interface for the ErrEducationBackend backend.
|
||||||
|
func (i *ErrEducationBackend) AddClassesToEducationSchool(ctx context.Context, schoolNumberOrID string, memberIDs []string) error {
|
||||||
|
return errNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveClassFromEducationSchool implements the EducationBackend interface for the ErrEducationBackend backend.
|
||||||
|
func (i *ErrEducationBackend) RemoveClassFromEducationSchool(ctx context.Context, schoolNumberOrID string, memberID string) error {
|
||||||
|
return errNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
// AddUsersToEducationSchool adds new members (reference by a slice of IDs) to supplied school in the identity backend.
|
// AddUsersToEducationSchool adds new members (reference by a slice of IDs) to supplied school in the identity backend.
|
||||||
func (i *ErrEducationBackend) AddUsersToEducationSchool(ctx context.Context, schoolID string, memberID []string) error {
|
func (i *ErrEducationBackend) AddUsersToEducationSchool(ctx context.Context, schoolID string, memberID []string) error {
|
||||||
return errNotImplemented
|
return errNotImplemented
|
||||||
|
|||||||
@@ -344,8 +344,8 @@ func (i *LDAP) getEducationClassLDAPDN(class libregraph.EducationClass) string {
|
|||||||
func (i *LDAP) getEducationClassByID(nameOrID string, requestMembers bool) (*ldap.Entry, error) {
|
func (i *LDAP) getEducationClassByID(nameOrID string, requestMembers bool) (*ldap.Entry, error) {
|
||||||
return i.getEducationObjectByNameOrID(
|
return i.getEducationObjectByNameOrID(
|
||||||
nameOrID,
|
nameOrID,
|
||||||
i.groupAttributeMap.name,
|
i.userAttributeMap.id,
|
||||||
i.groupAttributeMap.id,
|
i.educationConfig.classAttributeMap.externalID,
|
||||||
i.groupFilter,
|
i.groupFilter,
|
||||||
i.educationConfig.classObjectClass,
|
i.educationConfig.classObjectClass,
|
||||||
i.groupBaseDN,
|
i.groupBaseDN,
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ func (i *LDAP) GetEducationSchoolUsers(ctx context.Context, schoolNumberOrID str
|
|||||||
logger.Debug().Str("backend", "ldap").Msg("GetEducationSchoolUsers")
|
logger.Debug().Str("backend", "ldap").Msg("GetEducationSchoolUsers")
|
||||||
|
|
||||||
entries, err := i.getEducationSchoolEntries(
|
entries, err := i.getEducationSchoolEntries(
|
||||||
schoolNumberOrID, i.userFilter, i.educationConfig.userObjectClass, i.userBaseDN, i.userScope, i.getUserAttrTypes(), logger,
|
schoolNumberOrID, i.userFilter, i.educationConfig.userObjectClass, i.userBaseDN, i.userScope, i.getEducationUserAttrTypes(), logger,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -17,6 +17,20 @@ type EducationBackend struct {
|
|||||||
mock.Mock
|
mock.Mock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddClassesToEducationSchool provides a mock function with given fields: ctx, schoolNumberOrID, memberIDs
|
||||||
|
func (_m *EducationBackend) AddClassesToEducationSchool(ctx context.Context, schoolNumberOrID string, memberIDs []string) error {
|
||||||
|
ret := _m.Called(ctx, schoolNumberOrID, memberIDs)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, []string) error); ok {
|
||||||
|
r0 = rf(ctx, schoolNumberOrID, memberIDs)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
// AddUsersToEducationSchool provides a mock function with given fields: ctx, schoolID, memberID
|
// AddUsersToEducationSchool provides a mock function with given fields: ctx, schoolID, memberID
|
||||||
func (_m *EducationBackend) AddUsersToEducationSchool(ctx context.Context, schoolID string, memberID []string) error {
|
func (_m *EducationBackend) AddUsersToEducationSchool(ctx context.Context, schoolID string, memberID []string) error {
|
||||||
ret := _m.Called(ctx, schoolID, memberID)
|
ret := _m.Called(ctx, schoolID, memberID)
|
||||||
@@ -234,6 +248,29 @@ func (_m *EducationBackend) GetEducationSchool(ctx context.Context, nameOrID str
|
|||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetEducationSchoolClasses provides a mock function with given fields: ctx, schoolNumberOrID
|
||||||
|
func (_m *EducationBackend) GetEducationSchoolClasses(ctx context.Context, schoolNumberOrID string) ([]*libregraph.EducationClass, error) {
|
||||||
|
ret := _m.Called(ctx, schoolNumberOrID)
|
||||||
|
|
||||||
|
var r0 []*libregraph.EducationClass
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string) []*libregraph.EducationClass); ok {
|
||||||
|
r0 = rf(ctx, schoolNumberOrID)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]*libregraph.EducationClass)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
||||||
|
r1 = rf(ctx, schoolNumberOrID)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// GetEducationSchoolUsers provides a mock function with given fields: ctx, id
|
// GetEducationSchoolUsers provides a mock function with given fields: ctx, id
|
||||||
func (_m *EducationBackend) GetEducationSchoolUsers(ctx context.Context, id string) ([]*libregraph.EducationUser, error) {
|
func (_m *EducationBackend) GetEducationSchoolUsers(ctx context.Context, id string) ([]*libregraph.EducationUser, error) {
|
||||||
ret := _m.Called(ctx, id)
|
ret := _m.Called(ctx, id)
|
||||||
@@ -326,6 +363,20 @@ func (_m *EducationBackend) GetEducationUsers(ctx context.Context, queryParam ur
|
|||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RemoveClassFromEducationSchool provides a mock function with given fields: ctx, schoolNumberOrID, memberID
|
||||||
|
func (_m *EducationBackend) RemoveClassFromEducationSchool(ctx context.Context, schoolNumberOrID string, memberID string) error {
|
||||||
|
ret := _m.Called(ctx, schoolNumberOrID, memberID)
|
||||||
|
|
||||||
|
var r0 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
|
||||||
|
r0 = rf(ctx, schoolNumberOrID, memberID)
|
||||||
|
} else {
|
||||||
|
r0 = ret.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0
|
||||||
|
}
|
||||||
|
|
||||||
// RemoveUserFromEducationSchool provides a mock function with given fields: ctx, schoolID, memberID
|
// RemoveUserFromEducationSchool provides a mock function with given fields: ctx, schoolID, memberID
|
||||||
func (_m *EducationBackend) RemoveUserFromEducationSchool(ctx context.Context, schoolID string, memberID string) error {
|
func (_m *EducationBackend) RemoveUserFromEducationSchool(ctx context.Context, schoolID string, memberID string) error {
|
||||||
ret := _m.Called(ctx, schoolID, memberID)
|
ret := _m.Called(ctx, schoolID, memberID)
|
||||||
|
|||||||
@@ -423,6 +423,176 @@ func (g Graph) DeleteEducationSchoolUser(w http.ResponseWriter, r *http.Request)
|
|||||||
render.NoContent(w, r)
|
render.NoContent(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetEducationSchoolUsers implements the Service interface.
|
||||||
|
func (g Graph) GetEducationSchoolClasses(w http.ResponseWriter, r *http.Request) {
|
||||||
|
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||||
|
logger.Info().Msg("calling get school classes")
|
||||||
|
schoolID := chi.URLParam(r, "schoolID")
|
||||||
|
schoolID, err := url.PathUnescape(schoolID)
|
||||||
|
if err != nil {
|
||||||
|
logger.Debug().Str("id", schoolID).Msg("could not get school users: unescaping school id failed")
|
||||||
|
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping school id failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if schoolID == "" {
|
||||||
|
logger.Debug().Msg("could not get school users: missing school id")
|
||||||
|
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing school id")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug().Str("id", schoolID).Msg("calling get school classes on backend")
|
||||||
|
classes, err := g.identityEducationBackend.GetEducationSchoolClasses(r.Context(), schoolID)
|
||||||
|
if err != nil {
|
||||||
|
logger.Debug().Err(err).Msg("could not get school classes: backend error")
|
||||||
|
var errcode errorcode.Error
|
||||||
|
if errors.As(err, &errcode) {
|
||||||
|
errcode.Render(w, r)
|
||||||
|
} else {
|
||||||
|
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
render.Status(r, http.StatusOK)
|
||||||
|
render.JSON(w, r, classes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostEducationSchoolUser implements the Service interface.
|
||||||
|
func (g Graph) PostEducationSchoolClass(w http.ResponseWriter, r *http.Request) {
|
||||||
|
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||||
|
logger.Info().Msg("Calling post school class")
|
||||||
|
|
||||||
|
schoolID := chi.URLParam(r, "schoolID")
|
||||||
|
schoolID, err := url.PathUnescape(schoolID)
|
||||||
|
if err != nil {
|
||||||
|
logger.Debug().
|
||||||
|
Err(err).
|
||||||
|
Str("id", schoolID).
|
||||||
|
Msg("could not add class to school: unescaping school id failed")
|
||||||
|
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping school id failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if schoolID == "" {
|
||||||
|
logger.Debug().Msg("could not add school class: missing school id")
|
||||||
|
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing school id")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
memberRef := libregraph.NewMemberReference()
|
||||||
|
err = json.NewDecoder(r.Body).Decode(memberRef)
|
||||||
|
if err != nil {
|
||||||
|
logger.Debug().
|
||||||
|
Err(err).
|
||||||
|
Interface("body", r.Body).
|
||||||
|
Msg("could not add school class: invalid request body")
|
||||||
|
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("invalid request body: %s", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
memberRefURL, ok := memberRef.GetOdataIdOk()
|
||||||
|
if !ok {
|
||||||
|
logger.Debug().Msg("could not add school class: @odata.id reference is missing")
|
||||||
|
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "@odata.id reference is missing")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
memberType, id, err := g.parseMemberRef(*memberRefURL)
|
||||||
|
if err != nil {
|
||||||
|
logger.Debug().Err(err).Msg("could not add school class: error parsing @odata.id url")
|
||||||
|
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "Error parsing @odata.id url")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// The MS Graph spec allows "directoryObject", "user", "school" and "organizational Contact"
|
||||||
|
// we restrict this to users for now. Might add Schools as members later
|
||||||
|
if memberType != "classes" {
|
||||||
|
logger.Debug().Str("type", memberType).Msg("could not add school class: Only classes are allowed as school members")
|
||||||
|
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "Only classes are allowed as school members")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug().Str("memberType", memberType).Str("id", id).Msg("calling add class on backend")
|
||||||
|
err = g.identityEducationBackend.AddClassesToEducationSchool(r.Context(), schoolID, []string{id})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Debug().Err(err).Msg("could not add school class: backend error")
|
||||||
|
var errcode errorcode.Error
|
||||||
|
if errors.As(err, &errcode) {
|
||||||
|
errcode.Render(w, r)
|
||||||
|
} else {
|
||||||
|
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO requires reva changes
|
||||||
|
e := events.SchoolMemberAdded{SchoolID: schoolID, UserID: id}
|
||||||
|
if currentUser, ok := ctxpkg.ContextGetUser(r.Context()); ok {
|
||||||
|
e.Executant = currentUser.GetId()
|
||||||
|
}
|
||||||
|
g.publishEvent(e)
|
||||||
|
*/
|
||||||
|
|
||||||
|
render.Status(r, http.StatusNoContent)
|
||||||
|
render.NoContent(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteEducationSchoolUser implements the Service interface.
|
||||||
|
func (g Graph) DeleteEducationSchoolClass(w http.ResponseWriter, r *http.Request) {
|
||||||
|
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||||
|
logger.Info().Msg("calling delete school class")
|
||||||
|
|
||||||
|
schoolID := chi.URLParam(r, "schoolID")
|
||||||
|
schoolID, err := url.PathUnescape(schoolID)
|
||||||
|
if err != nil {
|
||||||
|
logger.Debug().Err(err).Str("id", schoolID).Msg("could not delete school class: unescaping school id failed")
|
||||||
|
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping school id failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if schoolID == "" {
|
||||||
|
logger.Debug().Msg("could not delete school class: missing school id")
|
||||||
|
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing school id")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
classID := chi.URLParam(r, "classID")
|
||||||
|
classID, err = url.PathUnescape(classID)
|
||||||
|
if err != nil {
|
||||||
|
logger.Debug().Err(err).Str("id", classID).Msg("could not delete school class: unescaping class id failed")
|
||||||
|
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping class id failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if classID == "" {
|
||||||
|
logger.Debug().Msg("could not delete school class: missing class id")
|
||||||
|
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing class id")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Debug().Str("schoolID", schoolID).Str("userID", classID).Msg("calling delete class on backend")
|
||||||
|
err = g.identityEducationBackend.RemoveClassFromEducationSchool(r.Context(), schoolID, classID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Debug().Err(err).Msg("could not delete school class: backend error")
|
||||||
|
var errcode errorcode.Error
|
||||||
|
if errors.As(err, &errcode) {
|
||||||
|
errcode.Render(w, r)
|
||||||
|
} else {
|
||||||
|
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO requires reva changes
|
||||||
|
e := events.SchoolMemberRemoved{SchoolID: schoolID, UserID: userID}
|
||||||
|
if currentUser, ok := ctxpkg.ContextGetUser(r.Context()); ok {
|
||||||
|
e.Executant = currentUser.GetId()
|
||||||
|
}
|
||||||
|
g.publishEvent(e)
|
||||||
|
*/
|
||||||
|
|
||||||
|
render.Status(r, http.StatusNoContent)
|
||||||
|
render.NoContent(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
func sortEducationSchools(req *godata.GoDataRequest, schools []*libregraph.EducationSchool) ([]*libregraph.EducationSchool, error) {
|
func sortEducationSchools(req *godata.GoDataRequest, schools []*libregraph.EducationSchool) ([]*libregraph.EducationSchool, error) {
|
||||||
if req.Query.OrderBy == nil || len(req.Query.OrderBy.OrderByItems) != 1 {
|
if req.Query.OrderBy == nil || len(req.Query.OrderBy.OrderByItems) != 1 {
|
||||||
return schools, nil
|
return schools, nil
|
||||||
|
|||||||
@@ -241,6 +241,11 @@ func NewService(opts ...Option) (Graph, error) {
|
|||||||
r.Post("/$ref", svc.PostEducationSchoolUser)
|
r.Post("/$ref", svc.PostEducationSchoolUser)
|
||||||
r.Delete("/{userID}/$ref", svc.DeleteEducationSchoolUser)
|
r.Delete("/{userID}/$ref", svc.DeleteEducationSchoolUser)
|
||||||
})
|
})
|
||||||
|
r.Route("/classes", func(r chi.Router) {
|
||||||
|
r.Get("/", svc.GetEducationSchoolClasses)
|
||||||
|
r.Post("/$ref", svc.PostEducationSchoolClass)
|
||||||
|
r.Delete("/{classID}/$ref", svc.DeleteEducationSchoolClass)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
r.Route("/users", func(r chi.Router) {
|
r.Route("/users", func(r chi.Router) {
|
||||||
|
|||||||
Reference in New Issue
Block a user