Skip to content

Commit

Permalink
Allow booking by third-party user
Browse files Browse the repository at this point in the history
  • Loading branch information
zdevaty committed May 14, 2024
1 parent efe0e64 commit a2e6ab8
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 53 deletions.
27 changes: 19 additions & 8 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,12 +289,13 @@ func cancelInEWS(book model.Booking, config apiserver.Configuration) {
if err != nil {
log.Error("conf", "getting booking for Eliona ID %v: %v", book.ElionaID, err)
return
} else if !booking.ExchangeID.Valid || !booking.ExchangeUID.Valid {
log.Error("db", "cancelling booking: booking %v does not have exchangeID or UID", booking.ID)
} else if !booking.ExchangeID.Valid || !booking.ExchangeUID.Valid || !booking.ExchangeMailbox.Valid {
log.Error("db", "cancelling booking: booking %v does not have exchangeID, UID or Mailbox", booking.ID)
return
}
book.ExchangeIDInResourceMailbox = booking.ExchangeID.String
book.ExchangeUID = booking.ExchangeUID.String
book.OrganizerEmail = booking.ExchangeMailbox.String
if err := ewsHelper.CancelEvent(book); err != nil {
log.Error("ews", "cancelling event: %v", err)
return
Expand All @@ -309,15 +310,19 @@ func bookInEWS(book model.Booking, config apiserver.Configuration) {
log.Error("conf", "getting asset ID %v: %v", book.AssetID, err)
return
}
createAppointment(asset.ProviderID, book, config)
}

func createAppointment(assetEmail string, book model.Booking, config apiserver.Configuration) {
// We want to book on behalf of the organizer, thus we need to create a helper for each booking.
ewsHelper := ews.NewEWSHelper(*config.ClientId, *config.TenantId, *config.ClientSecret, book.OrganizerEmail)
app := ews.Appointment{
Organizer: book.OrganizerEmail,
Subject: "Eliona booking",
Start: book.Start,
End: book.End,
Location: asset.ProviderID,
Attendees: []string{asset.ProviderID},
Location: assetEmail,
Attendees: []string{assetEmail},
}
a, err := ewsHelper.CreateAppointment(app)
book.ExchangeUID = a.ExchangeUID
Expand All @@ -333,8 +338,13 @@ func bookInEWS(book model.Booking, config apiserver.Configuration) {
return
}
log.Debug("ews", "booking for %v was conflicting; cancelled", book.OrganizerEmail)
} else if errors.Is(err, ews.ErrNonExistentMailbox) && book.OrganizerEmail != *config.ServiceUserUPN {
log.Debug("ews", "booking for %v will be booked by a service user", book.OrganizerEmail)
book.OrganizerEmail = *config.ServiceUserUPN
createAppointment(assetEmail, book, config)
return
} else if err != nil {
log.Error("ews", "creating appointment: %v", err)
log.Error("ews", "creating appointment %v: %v", book.ElionaID, err)
log.Debug("ews", "cancelling booking %v", book.ElionaID)
bc := booking.NewClient(*config.BookingAppURL)
if err := bc.Cancel(book.ElionaID, "error"); err != nil {
Expand All @@ -345,9 +355,10 @@ func bookInEWS(book model.Booking, config apiserver.Configuration) {
}
log.Debug("ews", "created a booking for %v", book.OrganizerEmail)
b := appdb.Booking{
ExchangeID: null.StringFrom(book.ExchangeIDInResourceMailbox),
ExchangeUID: null.StringFrom(book.ExchangeUID),
BookingID: null.Int32From(book.ElionaID),
ExchangeID: null.StringFrom(book.ExchangeIDInResourceMailbox),
ExchangeUID: null.StringFrom(book.ExchangeUID),
ExchangeMailbox: null.StringFrom(book.OrganizerEmail),
BookingID: null.Int32From(book.ElionaID),
}
if err := conf.UpsertBooking(b); err != nil {
log.Error("conf", "upserting newly created booking: %v", err)
Expand Down
67 changes: 37 additions & 30 deletions appdb/booking.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions booking/booking.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@ func (c *client) Book(bookings []model.Booking) error {
return err
}
if err := conf.UpsertBooking(appdb.Booking{
ExchangeID: null.StringFrom(b.ExchangeIDInResourceMailbox),
ExchangeUID: null.StringFrom(b.ExchangeUID),
BookingID: null.Int32From(responseBooking.Id),
ExchangeID: null.StringFrom(b.ExchangeIDInResourceMailbox),
ExchangeUID: null.StringFrom(b.ExchangeUID),
ExchangeMailbox: null.StringFrom(b.OrganizerEmail),
BookingID: null.Int32From(responseBooking.Id),
}); err != nil {
return fmt.Errorf("upserting booking id: %v", err)
}
Expand Down
1 change: 1 addition & 0 deletions conf/init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ create table if not exists ews.booking
id bigserial primary key,
exchange_id text unique, -- Always from the resource's perspective
exchange_uid text unique, -- Unique identifier regardless of perspective
exchange_mailbox text,
booking_id int unique
);

Expand Down
45 changes: 33 additions & 12 deletions ews/ews.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import (
)

var ErrDeclined = errors.New("resource has declined invitation")
var ErrNonExistentMailbox = errors.New("the SMTP address has no mailbox associated with it within this Exchange server")

var errNotFound = errors.New("entity not found")

Expand Down Expand Up @@ -83,6 +84,19 @@ func (h *EWSHelper) sendRequest(xmlBody string) ([]byte, error) {
return responseBody, nil
}

type soapFault struct {
Body struct {
Fault struct {
FaultCode string `xml:"faultcode"`
FaultString string `xml:"faultstring"`
Detail struct {
ResponseCode string `xml:"ResponseCode"`
Message string `xml:"Message"`
} `xml:"detail"`
} `xml:"Fault"`
} `xml:"Body"`
}

type roomsEnvelope struct {
XMLName xml.Name `xml:"Envelope"`
Body roomsBody `xml:"Body"`
Expand Down Expand Up @@ -271,18 +285,7 @@ func (h *EWSHelper) GetRoomAppointments(roomEmail string, syncState string) (new
}

// First, try to unmarshal into SOAPFault to see if there was an error.
var soapFault struct {
Body struct {
Fault struct {
FaultCode string `xml:"faultcode"`
FaultString string `xml:"faultstring"`
Detail struct {
ResponseCode string `xml:"ResponseCode"`
Message string `xml:"Message"`
} `xml:"detail"`
} `xml:"Fault"`
} `xml:"Body"`
}
var soapFault soapFault
if err := xml.Unmarshal(responseXML, &soapFault); err == nil && soapFault.Body.Fault.FaultCode != "" {
return nil, nil, nil, syncState, fmt.Errorf("SOAP fault: %s - %s", soapFault.Body.Fault.Detail.ResponseCode, soapFault.Body.Fault.Detail.Message)
}
Expand Down Expand Up @@ -425,6 +428,15 @@ func (h *EWSHelper) CreateAppointment(appointment Appointment) (booking model.Bo
return model.Booking{}, fmt.Errorf("requesting create appointment: %w", err)
}

// First, try to unmarshal into SOAPFault to see if there was an error.
var soapFault soapFault
if err := xml.Unmarshal(responseXML, &soapFault); err == nil && soapFault.Body.Fault.FaultCode != "" {
if soapFault.Body.Fault.Detail.ResponseCode == "ErrorNonExistentMailbox" {
return model.Booking{}, ErrNonExistentMailbox
}
return model.Booking{}, fmt.Errorf("SOAP fault: %s - %s", soapFault.Body.Fault.Detail.ResponseCode, soapFault.Body.Fault.Detail.Message)
}

var env appointmentCreated
if err := xml.Unmarshal(responseXML, &env); err != nil {
return model.Booking{}, fmt.Errorf("unmarshaling XML: %v", err)
Expand Down Expand Up @@ -525,6 +537,15 @@ func (h *EWSHelper) CancelEvent(event model.Booking) error {
return fmt.Errorf("requesting cancel event: %w", err)
}

// First, try to unmarshal into SOAPFault to see if there was an error.
var soapFault soapFault
if err := xml.Unmarshal(responseXML, &soapFault); err == nil && soapFault.Body.Fault.FaultCode != "" {
if soapFault.Body.Fault.FaultCode == "ErrorNonExistentMailbox" {
return ErrNonExistentMailbox
}
return fmt.Errorf("SOAP fault: %s - %s", soapFault.Body.Fault.Detail.ResponseCode, soapFault.Body.Fault.Detail.Message)
}

var response struct {
XMLName xml.Name `xml:"Envelope"`
Body struct {
Expand Down

0 comments on commit a2e6ab8

Please sign in to comment.