graph: Add support for listing/adding/removing teachers to a class

This commit is contained in:
Daniel Swärd
2023-02-06 12:16:04 +01:00
parent 26f7523ff8
commit 8e1a65fc29
10 changed files with 579 additions and 0 deletions
+7
View File
@@ -91,6 +91,13 @@ type EducationBackend interface {
GetEducationUser(ctx context.Context, nameOrID string) (*libregraph.EducationUser, error)
// GetEducationUsers lists all education users
GetEducationUsers(ctx context.Context) ([]*libregraph.EducationUser, error)
// GetEducationClassTeachers returns the EducationUser teachers for an EducationClass
GetEducationClassTeachers(ctx context.Context, classID string) ([]*libregraph.EducationUser, error)
// AddTeacherToEducationclass adds a teacher (by ID) to class in the identity backend.
AddTeacherToEducationClass(ctx context.Context, classID string, teacherID string) error
// RemoveTeacherFromEducationClass removes teacher (by ID) from a class
RemoveTeacherFromEducationClass(ctx context.Context, classID string, teacherID string) error
}
func CreateUserModelFromCS3(u *cs3.User) *libregraph.User {
@@ -118,3 +118,18 @@ func (i *ErrEducationBackend) GetEducationUser(ctx context.Context, nameOrID str
func (i *ErrEducationBackend) GetEducationUsers(ctx context.Context) ([]*libregraph.EducationUser, error) {
return nil, errNotImplemented
}
// GetEducationClassTeachers implements the EducationBackend interface for the ErrEducationBackend backend.
func (i *ErrEducationBackend) GetEducationClassTeachers(ctx context.Context, classID string) ([]*libregraph.EducationUser, error) {
return nil, errNotImplemented
}
// AddTeacherToEducationclass implements the EducationBackend interface for the ErrEducationBackend backend.
func (i *ErrEducationBackend) AddTeacherToEducationClass(ctx context.Context, classID string, teacherID string) error {
return errNotImplemented
}
// RemoveTeacherFromEducationClass implements the EducationBackend interface for the ErrEducationBackend backend.
func (i *ErrEducationBackend) RemoveTeacherFromEducationClass(ctx context.Context, classID string, teacherID string) error {
return errNotImplemented
}
@@ -6,6 +6,7 @@ import (
"fmt"
"github.com/go-ldap/ldap/v3"
"github.com/libregraph/idm/pkg/ldapdn"
libregraph "github.com/owncloud/libre-graph-api-go"
oldap "github.com/owncloud/ocis/v2/ocis-pkg/ldap"
"github.com/owncloud/ocis/v2/services/graph/pkg/service/v0/errorcode"
@@ -14,12 +15,14 @@ import (
type educationClassAttributeMap struct {
externalID string
classification string
teachers string
}
func newEducationClassAttributeMap() educationClassAttributeMap {
return educationClassAttributeMap{
externalID: "ocEducationExternalId",
classification: "ocEducationClassType",
teachers: "ocEducationTeacherMember",
}
}
@@ -283,6 +286,7 @@ func (i *LDAP) getEducationClassAttrTypes(requestMembers bool) []string {
i.educationConfig.classAttributeMap.classification,
i.educationConfig.classAttributeMap.externalID,
i.educationConfig.memberOfSchoolAttribute,
i.educationConfig.classAttributeMap.teachers,
}
if requestMembers {
attrs = append(attrs, i.groupAttributeMap.member)
@@ -337,3 +341,174 @@ func (i *LDAP) getEducationClassByID(nameOrID string, requestMembers bool) (*lda
i.getEducationClassAttrTypes(requestMembers),
)
}
// GetEducationClassTeachers returns the EducationUser teachers for an EducationClass
func (i *LDAP) GetEducationClassTeachers(ctx context.Context, classID string) ([]*libregraph.EducationUser, error) {
logger := i.logger.SubloggerWithRequestID(ctx)
class, err := i.getEducationClassByID(classID, false)
if err != nil {
logger.Debug().Err(err).Msg("could not get class: backend error")
return nil, err
}
teacherEntries, err := i.expandLDAPClassTeachers(ctx, class)
result := make([]*libregraph.EducationUser, 0, len(teacherEntries))
if err != nil {
return nil, err
}
for _, teacher := range teacherEntries {
if u := i.createEducationUserModelFromLDAP(teacher); u != nil {
result = append(result, u)
}
}
return result, nil
}
func (i *LDAP) expandLDAPClassTeachers(ctx context.Context, e *ldap.Entry) ([]*ldap.Entry, error) {
logger := i.logger.SubloggerWithRequestID(ctx)
logger.Debug().Str("backend", "ldap").Msg("expandLDAPClassTeachers")
result := []*ldap.Entry{}
for _, teacherDN := range e.GetEqualFoldAttributeValues(i.educationConfig.classAttributeMap.teachers) {
if teacherDN == "" {
continue
}
logger.Debug().Str("teacherDN", teacherDN).Msg("lookup")
ue, err := i.getUserByDN(teacherDN)
if err != nil {
// Ignore errors when reading a specific teacher fails, just log them and continue
logger.Debug().Err(err).Str("teacher", teacherDN).Msg("error reading class teacher")
continue
}
result = append(result, ue)
}
return result, nil
}
// AddTeacherToEducationclass adds a teacher (by ID) to class in the identity backend.
func (i *LDAP) AddTeacherToEducationClass(ctx context.Context, classID string, teacherID string) error {
logger := i.logger.SubloggerWithRequestID(ctx)
class, err := i.getEducationClassByID(classID, false)
if err != nil {
logger.Debug().Err(err).Msg("could not get class: backend error")
return err
}
logger.Debug().Str("classDn", class.DN).Msg("got a class")
teacher, err := i.getEducationUserByNameOrID(teacherID)
if err != nil {
logger.Debug().Err(err).Msg("could not get education user: error fetching education user from backend")
return err
}
logger.Debug().Str("userDn", teacher.DN).Msg("got a user")
mr := ldap.ModifyRequest{DN: class.DN}
// Handle empty teacher list
current := class.GetEqualFoldAttributeValues(i.educationConfig.classAttributeMap.teachers)
if len(current) == 1 && current[0] == "" {
mr.Delete(i.educationConfig.classAttributeMap.teachers, []string{""})
}
// Create a Set of current teachers
currentSet := make(map[string]struct{}, len(current))
for _, currentTeacher := range current {
if currentTeacher == "" {
continue
}
nCurrentTeacher, err := ldapdn.ParseNormalize(currentTeacher)
if err != nil {
// Couldn't parse teacher value as a DN, skipping
logger.Warn().Str("teacherDN", currentTeacher).Err(err).Msg("Couldn't parse DN")
continue
}
currentSet[nCurrentTeacher] = struct{}{}
}
var newTeacherDN []string
nDN, err := ldapdn.ParseNormalize(teacher.DN)
if err != nil {
logger.Error().Str("new teacher", teacher.DN).Err(err).Msg("Couldn't parse DN")
return err
}
if _, present := currentSet[nDN]; !present {
newTeacherDN = append(newTeacherDN, teacher.DN)
} else {
logger.Debug().Str("teacherDN", teacher.DN).Msg("Member already present in group. Skipping")
}
if len(newTeacherDN) > 0 {
mr.Add(i.educationConfig.classAttributeMap.teachers, newTeacherDN)
if err := i.conn.Modify(&mr); err != nil {
return err
}
}
return nil
}
// RemoveTeacherFromEducationClass removes teacher (by ID) from a class
func (i *LDAP) RemoveTeacherFromEducationClass(ctx context.Context, classID string, teacherID string) error {
logger := i.logger.SubloggerWithRequestID(ctx)
class, err := i.getEducationClassByID(classID, false)
if err != nil {
logger.Debug().Err(err).Msg("could not get class: backend error")
return err
}
teacher, err := i.getEducationUserByNameOrID(teacherID)
if err != nil {
logger.Debug().Err(err).Msg("could not get education user: error fetching education user from backend")
return err
}
if mr, err := i.removeTeacherFromClassEntry(class, teacher.DN); err == nil {
return i.conn.Modify(mr)
}
return nil
}
// removeTeacherFromClassEntry creates an LDAP Modify request (not sending it)
// that would update the supplied entry to remove the specified teacher from the
// class
func (i *LDAP) removeTeacherFromClassEntry(class *ldap.Entry, teacherDN string) (*ldap.ModifyRequest, error) {
nOldTeacherDN, err := ldapdn.ParseNormalize(teacherDN)
if err != nil {
return nil, err
}
teachers := class.GetEqualFoldAttributeValues(i.educationConfig.classAttributeMap.teachers)
found := false
for _, teacher := range teachers {
if teacher == "" {
continue
}
if nTeacher, err := ldapdn.ParseNormalize(teacher); err != nil {
// We couldn't parse the teacher value as a DN. Let's keep it
// as it is but log a warning
i.logger.Warn().Str("teacherDN", teacher).Err(err).Msg("Couldn't parse DN")
continue
} else {
if nTeacher == nOldTeacherDN {
found = true
}
}
}
if !found {
i.logger.Debug().Str("backend", "ldap").Str("groupdn", class.DN).Str("teacher", teacherDN).
Msg("The target is not a teacher of the class")
return nil, ErrNotFound
}
mr := ldap.ModifyRequest{DN: class.DN}
if len(teachers) == 1 {
mr.Add(i.educationConfig.classAttributeMap.teachers, []string{""})
}
mr.Delete(i.educationConfig.classAttributeMap.teachers, []string{teacherDN})
return &mr, nil
}
@@ -29,6 +29,20 @@ func (_m *EducationBackend) AddClassesToEducationSchool(ctx context.Context, sch
return r0
}
// AddTeacherToEducationClass provides a mock function with given fields: ctx, classID, teacherID
func (_m *EducationBackend) AddTeacherToEducationClass(ctx context.Context, classID string, teacherID string) error {
ret := _m.Called(ctx, classID, teacherID)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
r0 = rf(ctx, classID, teacherID)
} else {
r0 = ret.Error(0)
}
return r0
}
// AddUsersToEducationSchool provides a mock function with given fields: ctx, schoolID, memberID
func (_m *EducationBackend) AddUsersToEducationSchool(ctx context.Context, schoolID string, memberID []string) error {
ret := _m.Called(ctx, schoolID, memberID)
@@ -200,6 +214,29 @@ func (_m *EducationBackend) GetEducationClassMembers(ctx context.Context, nameOr
return r0, r1
}
// GetEducationClassTeachers provides a mock function with given fields: ctx, classID
func (_m *EducationBackend) GetEducationClassTeachers(ctx context.Context, classID string) ([]*libregraph.EducationUser, error) {
ret := _m.Called(ctx, classID)
var r0 []*libregraph.EducationUser
if rf, ok := ret.Get(0).(func(context.Context, string) []*libregraph.EducationUser); ok {
r0 = rf(ctx, classID)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*libregraph.EducationUser)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
r1 = rf(ctx, classID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetEducationClasses provides a mock function with given fields: ctx
func (_m *EducationBackend) GetEducationClasses(ctx context.Context) ([]*libregraph.EducationClass, error) {
ret := _m.Called(ctx)
@@ -375,6 +412,20 @@ func (_m *EducationBackend) RemoveClassFromEducationSchool(ctx context.Context,
return r0
}
// RemoveTeacherFromEducationClass provides a mock function with given fields: ctx, classID, teacherID
func (_m *EducationBackend) RemoveTeacherFromEducationClass(ctx context.Context, classID string, teacherID string) error {
ret := _m.Called(ctx, classID, teacherID)
var r0 error
if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok {
r0 = rf(ctx, classID, teacherID)
} else {
r0 = ret.Error(0)
}
return r0
}
// RemoveUserFromEducationSchool provides a mock function with given fields: ctx, schoolID, memberID
func (_m *EducationBackend) RemoveUserFromEducationSchool(ctx context.Context, schoolID string, memberID string) error {
ret := _m.Called(ctx, schoolID, memberID)
@@ -443,6 +443,168 @@ func (g Graph) DeleteEducationClassMember(w http.ResponseWriter, r *http.Request
render.NoContent(w, r)
}
// GetEducationClassTeachers implements the Service interface.
func (g Graph) GetEducationClassTeachers(w http.ResponseWriter, r *http.Request) {
logger := g.logger.SubloggerWithRequestID(r.Context())
logger.Info().Msg("calling get class teachers")
classID := chi.URLParam(r, "classID")
classID, err := url.PathUnescape(classID)
if err != nil {
logger.Debug().Str("id", classID).Msg("could not get class teachers: unescaping class id failed")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping class id failed")
return
}
if classID == "" {
logger.Debug().Msg("could not get class teachers: missing class id")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing class id")
return
}
logger.Debug().Str("id", classID).Msg("calling get class teachers on backend")
teachers, err := g.identityEducationBackend.GetEducationClassTeachers(r.Context(), classID)
// teachers, err := g.identityEducationBackend.GetEducationClassMembers(r.Context(), classID)
if err != nil {
logger.Debug().Err(err).Msg("could not get class teachers: 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, teachers)
}
// PostEducationClassTeacher implements the Service interface.
func (g Graph) PostEducationClassTeacher(w http.ResponseWriter, r *http.Request) {
logger := g.logger.SubloggerWithRequestID(r.Context())
logger.Info().Msg("Calling post class teacher")
classID := chi.URLParam(r, "classID")
classID, err := url.PathUnescape(classID)
if err != nil {
logger.Debug().
Err(err).
Str("id", classID).
Msg("could not add teacher to class: unescaping class id failed")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping class id failed")
return
}
if classID == "" {
logger.Debug().Msg("could not add class teacher: missing class id")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing class 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 class teacher: 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 class teacher: @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 class teacher: 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", "class" and "organizational Contact"
// we restrict this to users for now. Might add EducationClass as members later
if memberType != memberTypeUsers {
logger.Debug().Str("type", memberType).Msg("could not add class member: Only users are allowed as class teachers")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "Only users are allowed as class teachers")
return
}
logger.Debug().Str("memberType", memberType).Str("id", id).Msg("calling add teacher on backend")
err = g.identityEducationBackend.AddTeacherToEducationClass(r.Context(), classID, id)
if err != nil {
logger.Debug().Err(err).Msg("could not add class teacher: 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
currentUser := revactx.ContextMustGetUser(r.Context())
g.publishEvent(events.EducationClassTeacherAdded{Executant: currentUser.Id, EducationClassID: classID, UserID: id})
*/
render.Status(r, http.StatusNoContent)
render.NoContent(w, r)
}
// DeleteEducationClassTeacher implements the Service interface.
func (g Graph) DeleteEducationClassTeacher(w http.ResponseWriter, r *http.Request) {
logger := g.logger.SubloggerWithRequestID(r.Context())
logger.Info().Msg("calling delete class teacher")
classID := chi.URLParam(r, "classID")
classID, err := url.PathUnescape(classID)
if err != nil {
logger.Debug().Err(err).Str("id", classID).Msg("could not delete class teacher: unescaping class id failed")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping class id failed")
return
}
if classID == "" {
logger.Debug().Msg("could not delete class teacher: missing class id")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing class id")
return
}
teacherID := chi.URLParam(r, "teacherID")
teacherID, err = url.PathUnescape(teacherID)
if err != nil {
logger.Debug().Err(err).Str("id", teacherID).Msg("could not delete class teacher: unescaping teacher id failed")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping teacher id failed")
return
}
if teacherID == "" {
logger.Debug().Msg("could not delete class teacher: missing teacher id")
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "missing teacher id")
return
}
logger.Debug().Str("classID", classID).Str("teacherID", teacherID).Msg("calling delete teacher on backend")
err = g.identityEducationBackend.RemoveTeacherFromEducationClass(r.Context(), classID, teacherID)
if err != nil {
logger.Debug().Err(err).Msg("could not delete class teacher: 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
currentUser := revactx.ContextMustGetUser(r.Context())
g.publishEvent(events.EducationClassTeacherRemoved{Executant: currentUser.Id, EducationClassID: classID, UserID: teacherID})
*/
render.Status(r, http.StatusNoContent)
render.NoContent(w, r)
}
func sortClasses(req *godata.GoDataRequest, classes []*libregraph.EducationClass) ([]*libregraph.EducationClass, error) {
if req.Query.OrderBy == nil || len(req.Query.OrderBy.OrderByItems) != 1 {
return classes, nil
@@ -530,4 +530,119 @@ var _ = Describe("EducationClass", func() {
identityBackend.AssertNumberOfCalls(GinkgoT(), "RemoveMemberFromGroup", 1)
})
})
Describe("GetEducationClassTeachers", func() {
It("gets the list of teachers", func() {
user := libregraph.NewEducationUser()
user.SetId("user")
identityEducationBackend.On("GetEducationClassTeachers", mock.Anything, mock.Anything, mock.Anything).
Return([]*libregraph.EducationUser{user}, nil)
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/education/classes/{classID}/teachers", nil)
rctx := chi.NewRouteContext()
rctx.URLParams.Add("classID", *newClass.Id)
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
svc.GetEducationClassTeachers(rr, r)
Expect(rr.Code).To(Equal(http.StatusOK))
data, err := io.ReadAll(rr.Body)
Expect(err).ToNot(HaveOccurred())
var teachers []*libregraph.EducationUser
err = json.Unmarshal(data, &teachers)
Expect(err).ToNot(HaveOccurred())
Expect(len(teachers)).To(Equal(1))
Expect(teachers[0].GetId()).To(Equal("user"))
})
})
Describe("PostEducationClassTeacher", func() {
It("fails on invalid body", func() {
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/classes/{classID}/teachers", bytes.NewBufferString("{invalid"))
rctx := chi.NewRouteContext()
rctx.URLParams.Add("classID", *newClass.Id)
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
svc.PostEducationClassTeacher(rr, r)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
})
It("fails on missing teacher refs", func() {
member := libregraph.NewMemberReference()
data, err := json.Marshal(member)
Expect(err).ToNot(HaveOccurred())
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/classes/{classID}/teachers", bytes.NewBuffer(data))
rctx := chi.NewRouteContext()
rctx.URLParams.Add("classID", *newClass.Id)
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
svc.PostEducationClassTeacher(rr, r)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
})
It("fails on invalid teacher refs", func() {
member := libregraph.NewMemberReference()
member.SetOdataId("/invalidtype/user")
data, err := json.Marshal(member)
Expect(err).ToNot(HaveOccurred())
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/classes/{classID}/teachers", bytes.NewBuffer(data))
rctx := chi.NewRouteContext()
rctx.URLParams.Add("classID", *newClass.Id)
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
svc.PostEducationClassTeacher(rr, r)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
})
It("adds a new teacher", func() {
member := libregraph.NewMemberReference()
member.SetOdataId("/users/user")
data, err := json.Marshal(member)
Expect(err).ToNot(HaveOccurred())
identityEducationBackend.On("AddTeacherToEducationClass", mock.Anything, mock.Anything, mock.Anything).Return(nil)
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/education/classes/{classID}/teachers", bytes.NewBuffer(data))
rctx := chi.NewRouteContext()
rctx.URLParams.Add("classID", *newClass.Id)
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
svc.PostEducationClassTeacher(rr, r)
Expect(rr.Code).To(Equal(http.StatusNoContent))
identityEducationBackend.AssertNumberOfCalls(GinkgoT(), "AddTeacherToEducationClass", 1)
})
})
Describe("DeleteEducationClassTeacher", func() {
It("handles missing or empty teacher id", func() {
r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/education/classes/{classID}/teachers/{teacherID}/$ref", nil)
rctx := chi.NewRouteContext()
rctx.URLParams.Add("classID", *newClass.Id)
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
svc.DeleteEducationClassTeacher(rr, r)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
})
It("handles missing or empty teacher id", func() {
r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/education/classes/{classID}/teachers/{teacherID}/$ref", nil)
rctx := chi.NewRouteContext()
rctx.URLParams.Add("teacherID", "/users/user")
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
svc.DeleteEducationClassTeacher(rr, r)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
})
It("deletes teacher", func() {
identityEducationBackend.On("RemoveTeacherFromEducationClass", mock.Anything, mock.Anything, mock.Anything).Return(nil)
r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/education/classes/{classID}/teachers/{teacherID}/$ref", nil)
rctx := chi.NewRouteContext()
rctx.URLParams.Add("classID", *newClass.Id)
rctx.URLParams.Add("teacherID", "/users/user1")
r = r.WithContext(context.WithValue(revactx.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
svc.DeleteEducationClassTeacher(rr, r)
Expect(rr.Code).To(Equal(http.StatusNoContent))
identityEducationBackend.AssertNumberOfCalls(GinkgoT(), "RemoveTeacherFromEducationClass", 1)
})
})
})
@@ -293,3 +293,18 @@ func (i instrument) AssignTags(w http.ResponseWriter, r *http.Request) {
func (i instrument) UnassignTags(w http.ResponseWriter, r *http.Request) {
i.next.UnassignTags(w, r)
}
// GetEducationClassTeachers implements the Service interface.
func (i instrument) GetEducationClassTeachers(w http.ResponseWriter, r *http.Request) {
i.next.UnassignTags(w, r)
}
// PostEducationClassTeacher implements the Service interface.
func (i instrument) PostEducationClassTeacher(w http.ResponseWriter, r *http.Request) {
i.next.UnassignTags(w, r)
}
// DeleteEducationClassTeacher implements the Service interface.
func (i instrument) DeleteEducationClassTeacher(w http.ResponseWriter, r *http.Request) {
i.next.UnassignTags(w, r)
}
+15
View File
@@ -293,3 +293,18 @@ func (l logging) AssignTags(w http.ResponseWriter, r *http.Request) {
func (l logging) UnassignTags(w http.ResponseWriter, r *http.Request) {
l.next.UnassignTags(w, r)
}
// GetEducationClassTeachers implements the Service interface.
func (l logging) GetEducationClassTeachers(w http.ResponseWriter, r *http.Request) {
l.next.UnassignTags(w, r)
}
// PostEducationClassTeacher implements the Service interface.
func (l logging) PostEducationClassTeacher(w http.ResponseWriter, r *http.Request) {
l.next.UnassignTags(w, r)
}
// DeleteEducationClassTeacher implements the Service interface.
func (l logging) DeleteEducationClassTeacher(w http.ResponseWriter, r *http.Request) {
l.next.UnassignTags(w, r)
}
+9
View File
@@ -84,6 +84,10 @@ type Service interface {
PatchEducationUser(http.ResponseWriter, *http.Request)
DeleteEducationClassMember(w http.ResponseWriter, r *http.Request)
GetEducationClassTeachers(w http.ResponseWriter, r *http.Request)
PostEducationClassTeacher(w http.ResponseWriter, r *http.Request)
DeleteEducationClassTeacher(w http.ResponseWriter, r *http.Request)
GetDrives(w http.ResponseWriter, r *http.Request)
GetSingleDrive(w http.ResponseWriter, r *http.Request)
GetAllDrives(w http.ResponseWriter, r *http.Request)
@@ -270,6 +274,11 @@ func NewService(opts ...Option) (Graph, error) {
r.Post("/$ref", svc.PostEducationClassMember)
r.Delete("/{memberID}/$ref", svc.DeleteEducationClassMember)
})
r.Route("/teachers", func(r chi.Router) {
r.Get("/", svc.GetEducationClassTeachers)
r.Post("/$ref", svc.PostEducationClassTeacher)
r.Delete("/{teacherID}/$ref", svc.DeleteEducationClassTeacher)
})
})
})
})
+15
View File
@@ -289,3 +289,18 @@ func (t tracing) AssignTags(w http.ResponseWriter, r *http.Request) {
func (t tracing) UnassignTags(w http.ResponseWriter, r *http.Request) {
t.next.UnassignTags(w, r)
}
// GetEducationClassTeachers implements the Service interface.
func (t tracing) GetEducationClassTeachers(w http.ResponseWriter, r *http.Request) {
t.next.UnassignTags(w, r)
}
// PostEducationClassTeacher implements the Service interface.
func (t tracing) PostEducationClassTeacher(w http.ResponseWriter, r *http.Request) {
t.next.UnassignTags(w, r)
}
// DeleteEducationClassTeacher implements the Service interface.
func (t tracing) DeleteEducationClassTeacher(w http.ResponseWriter, r *http.Request) {
t.next.UnassignTags(w, r)
}