eintopf.info@v0.13.16/service/revent/transport.go (about)

     1  // Copyright (C) 2022 The Eintopf authors
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    15  
    16  package revent
    17  
    18  import (
    19  	"encoding/json"
    20  	"fmt"
    21  	"io"
    22  	"log"
    23  	"net/http"
    24  	"strconv"
    25  	"time"
    26  
    27  	"github.com/go-chi/chi/v5"
    28  
    29  	"eintopf.info/internal/crud"
    30  	"eintopf.info/internal/xerror"
    31  	"eintopf.info/internal/xhttp"
    32  	"eintopf.info/service/auth"
    33  	"eintopf.info/service/event"
    34  )
    35  
    36  // Router returns a new http router that handles crud RepeatingEvent requests for a given
    37  // RepeatingEvent service.
    38  func Router(service Service, authService auth.Service) func(chi.Router) {
    39  	router := &router{service}
    40  	return func(r chi.Router) {
    41  		r.Options("/", xhttp.CorsHandler)
    42  
    43  		// swagger:route GET /revents/ RepeatingEvents findRepeatingEvents
    44  		//
    45  		// Retrieves all RepeatingEvents.
    46  		//
    47  		// Parameters:
    48  		//   + name: offset
    49  		//     description: offset in event list
    50  		//     in: query
    51  		//     type: integer
    52  		//     required: false
    53  		//   + name: limit
    54  		//     description: limits the event list
    55  		//     in: query
    56  		//     type: integer
    57  		//     required: false
    58  		//   + name: sort
    59  		//     description: field that gets sorted
    60  		//     in: query
    61  		//     type: string
    62  		//     required: false
    63  		//   + name: order
    64  		//     description: sort order ("ASC" or "DESC")
    65  		//     in: query
    66  		//     type: string
    67  		//     required: false
    68  		//   + name: filters
    69  		//     description: filters get combined with AND logic
    70  		//     in: query
    71  		//     type: object
    72  		//     required: false
    73  		//
    74  		//     Responses:
    75  		//       200: findRepeatingEventsResponse
    76  		//       400: badRequest
    77  		//       401: unauthorizedError
    78  		//       500: internalError
    79  		//       500: internalError
    80  		//       503: serviceUnavailable
    81  		r.With(auth.MiddlewareWithOpts(authService, auth.MiddlewareOpts{Validate: false})).
    82  			Get("/", router.find)
    83  
    84  		// swagger:route POST /revents/ RepeatingEvent createRepeatingEvent
    85  		//
    86  		// Creates a new RepeatingEvent with the given data.
    87  		//     Security:
    88  		//       bearer: []
    89  		//
    90  		//     Responses:
    91  		//       200: createRepeatingEventResponse
    92  		//       400: badRequest
    93  		//       401: unauthorizedError
    94  		//       500: internalError
    95  		//       503: serviceUnavailable
    96  		r.With(auth.Middleware(authService)).
    97  			Post("/", router.create)
    98  
    99  		r.Options("/{id}", xhttp.CorsHandler)
   100  
   101  		// swagger:route GET /revents/{id} RepeatingEvent findRepeatingEvent
   102  		//
   103  		// Finds the RepeatingEvent with the given id.
   104  		//
   105  		//     Responses:
   106  		//       200: findRepeatingEventResponse
   107  		//       400: badRequest
   108  		//       401: unauthorizedError
   109  		//       500: internalError
   110  		//       503: serviceUnavailable
   111  		r.Get("/{id}", router.findByID)
   112  
   113  		// swagger:route PUT /revents/{id} RepeatingEvent updateRepeatingEvent
   114  		//
   115  		// Updates the RepeatingEvent with the given id.
   116  		//     Security:
   117  		//       bearer: []
   118  		//
   119  		//     Responses:
   120  		//       200: updateRepeatingEventResponse
   121  		//       400: badRequest
   122  		//       401: unauthorizedError
   123  		//       500: internalError
   124  		//       503: serviceUnavailable
   125  		r.With(auth.Middleware(authService)).
   126  			Put("/{id}", router.update)
   127  
   128  		// swagger:route DELETE /revents/{id} RepeatingEvent deleteRepeatingEvent
   129  		//
   130  		// Deltes the RepeatingEvent with the given id.
   131  		//     Security:
   132  		//       bearer: []
   133  		//
   134  		//     Responses:
   135  		//       200: deleteRepeatingEventResponse
   136  		//       400: badRequest
   137  		//       401: unauthorizedError
   138  		//       500: internalError
   139  		//       503: serviceUnavailable
   140  		r.With(auth.Middleware(authService)).
   141  			Delete("/{id}", router.delete)
   142  
   143  		r.Options("/{id}/generate", xhttp.CorsHandler)
   144  
   145  		// swagger:route GET /revents/{id}/generate RepeatingEvent generateRepeatingEvent
   146  		//
   147  		// Generates Events from the repeating event
   148  		//     Security:
   149  		//       bearer: []
   150  		//
   151  		//     Responses:
   152  		//       200: generateRepeatingEventResponse
   153  		//       400: badRequest
   154  		//       401: unauthorizedError
   155  		//       500: internalError
   156  		//       503: serviceUnavailable
   157  		r.With(auth.MiddlewareWithOpts(authService, auth.MiddlewareOpts{Validate: true})).
   158  			Get("/{id}/generate", router.generate)
   159  	}
   160  }
   161  
   162  type router struct {
   163  	s Service
   164  }
   165  
   166  func (router *router) find(w http.ResponseWriter, r *http.Request) {
   167  	params, err := readFindRequest(r)
   168  	if err != nil {
   169  		xhttp.WriteBadRequest(r.Context(), w, fmt.Errorf("failed to read findAll request: %s", err))
   170  		return
   171  	}
   172  	RepeatingEvents, totalRepeatingEvents, err := router.s.Find(r.Context(), params)
   173  	if err != nil {
   174  		xhttp.WriteError(r.Context(), w, err)
   175  		return
   176  	}
   177  	err = writeFindResponse(w, &findResponse{RepeatingEvents: RepeatingEvents, TotalRepeatingEvents: totalRepeatingEvents})
   178  	if err != nil {
   179  		log.Println(fmt.Errorf("failed to write findAll response: %s", err))
   180  		return
   181  	}
   182  }
   183  
   184  // swagger:response findRepeatingEventsResponse
   185  type findResponse struct {
   186  
   187  	// in:body
   188  	RepeatingEvents []*RepeatingEvent
   189  
   190  	// in:header
   191  	TotalRepeatingEvents int `json:"x-total-count"`
   192  }
   193  
   194  func readFindRequest(r *http.Request) (*crud.FindParams[FindFilters], error) {
   195  	params := &crud.FindParams[FindFilters]{}
   196  	var err error
   197  
   198  	params.Offset, err = xhttp.ReadQueryInt64(r, "offset")
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  	params.Limit, err = xhttp.ReadQueryInt64(r, "limit")
   203  	if err != nil {
   204  		return nil, err
   205  	}
   206  
   207  	params.Sort, err = xhttp.ReadQueryString(r, "sorting")
   208  	if err != nil {
   209  		return nil, err
   210  	}
   211  	order, err := xhttp.ReadQueryString(r, "order")
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  	params.Order = crud.SortOrder(order)
   216  
   217  	filters := &FindFilters{}
   218  	err = xhttp.ReadQueryJson(r, "filters", filters)
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  	params.Filters = filters
   223  
   224  	return params, nil
   225  }
   226  
   227  func writeFindResponse(w http.ResponseWriter, resp *findResponse) error {
   228  	data, err := json.Marshal(resp.RepeatingEvents)
   229  	if err != nil {
   230  		return err
   231  	}
   232  	w.Header().Add("Access-Control-Expose-Headers", "X-Total-Count")
   233  	w.Header().Add("X-Total-Count", strconv.Itoa(resp.TotalRepeatingEvents))
   234  	w.Write(data)
   235  	return nil
   236  }
   237  
   238  func (router *router) create(w http.ResponseWriter, r *http.Request) {
   239  	req, err := readCreateRequest(r)
   240  	if err != nil {
   241  		xhttp.WriteBadRequest(r.Context(), w, fmt.Errorf("failed to read create request: %s", err))
   242  		return
   243  	}
   244  	RepeatingEvent, err := router.s.Create(r.Context(), req.RepeatingEvent)
   245  	if err != nil {
   246  		xhttp.WriteError(r.Context(), w, err)
   247  		return
   248  	}
   249  	err = writeCreateResponse(w, &createResponse{RepeatingEvent})
   250  	if err != nil {
   251  		log.Println(fmt.Errorf("failed to write create response: %s", err))
   252  		return
   253  	}
   254  }
   255  
   256  // swagger:parameters createRepeatingEvent
   257  type createRequest struct {
   258  
   259  	// in:body
   260  	RepeatingEvent *NewRepeatingEvent
   261  }
   262  
   263  // swagger:response createRepeatingEventsResponse
   264  type createResponse struct {
   265  
   266  	// in:body
   267  	RepeatingEvent *RepeatingEvent
   268  }
   269  
   270  func readCreateRequest(r *http.Request) (*createRequest, error) {
   271  	data, err := io.ReadAll(r.Body)
   272  	if err != nil {
   273  		return nil, err
   274  	}
   275  	RepeatingEvent, err := decodeNewRepeatingEvent(data)
   276  	if err != nil {
   277  		return nil, err
   278  	}
   279  	return &createRequest{RepeatingEvent}, nil
   280  }
   281  
   282  func writeCreateResponse(w http.ResponseWriter, resp *createResponse) error {
   283  	data, err := encodeRepeatingEvent(resp.RepeatingEvent)
   284  	if err != nil {
   285  		return err
   286  	}
   287  	w.Write(data)
   288  	return nil
   289  }
   290  
   291  func (router *router) findByID(w http.ResponseWriter, r *http.Request) {
   292  	req, err := readFindByIDRequest(r)
   293  	if err != nil {
   294  		xhttp.WriteBadRequest(r.Context(), w, fmt.Errorf("failed to read find request: %s", err))
   295  		return
   296  	}
   297  	repeatingEvent, err := router.s.FindByID(r.Context(), req.ID)
   298  	if err != nil {
   299  		xhttp.WriteError(r.Context(), w, err)
   300  		return
   301  	}
   302  	err = writeFindByIDResponse(w, &findByIDResponse{repeatingEvent})
   303  	if err != nil {
   304  		log.Println(fmt.Errorf("failed to write find response: %s", err))
   305  		return
   306  	}
   307  }
   308  
   309  // swagger:parameters findRepeatingEvent
   310  type findByIDRequest struct {
   311  	// in:query
   312  	ID string `json:"id"`
   313  }
   314  
   315  // swagger:response findRepeatingEventResponse
   316  type findByIDResponse struct {
   317  
   318  	// in:body
   319  	RepeatingEvent *RepeatingEvent
   320  }
   321  
   322  func readFindByIDRequest(r *http.Request) (*findByIDRequest, error) {
   323  	id := chi.URLParam(r, "id")
   324  	return &findByIDRequest{id}, nil
   325  }
   326  
   327  func writeFindByIDResponse(w http.ResponseWriter, resp *findByIDResponse) error {
   328  	data, err := encodeRepeatingEvent(resp.RepeatingEvent)
   329  	if err != nil {
   330  		return err
   331  	}
   332  	w.Write(data)
   333  	return nil
   334  }
   335  
   336  func (router *router) update(w http.ResponseWriter, r *http.Request) {
   337  	req, err := readUpdateRequest(r)
   338  	if err != nil {
   339  		xhttp.WriteBadRequest(r.Context(), w, fmt.Errorf("failed to read update request: %s", err))
   340  		return
   341  	}
   342  	RepeatingEvent, err := router.s.Update(r.Context(), req.RepeatingEvent)
   343  	if err != nil {
   344  		xhttp.WriteError(r.Context(), w, err)
   345  		return
   346  	}
   347  	err = writeUpdateResponse(w, &updateResponse{RepeatingEvent})
   348  	if err != nil {
   349  		log.Println(fmt.Errorf("failed to write update response: %s", err))
   350  		return
   351  	}
   352  }
   353  
   354  // swagger:parameters updateRepeatingEvent
   355  type updateRequest struct {
   356  
   357  	// in:body
   358  	RepeatingEvent *RepeatingEvent
   359  }
   360  
   361  // swagger:response updateRepeatingEventResponse
   362  type updateResponse struct {
   363  
   364  	// in:body
   365  	RepeatingEvent *RepeatingEvent
   366  }
   367  
   368  func readUpdateRequest(r *http.Request) (*updateRequest, error) {
   369  	data, err := io.ReadAll(r.Body)
   370  	if err != nil {
   371  		return nil, err
   372  	}
   373  	RepeatingEvent, err := decodeRepeatingEvent(data)
   374  	if err != nil {
   375  		return nil, err
   376  	}
   377  	return &updateRequest{RepeatingEvent}, nil
   378  }
   379  
   380  func writeUpdateResponse(w http.ResponseWriter, resp *updateResponse) error {
   381  	data, err := encodeRepeatingEvent(resp.RepeatingEvent)
   382  	if err != nil {
   383  		return err
   384  	}
   385  	w.Write(data)
   386  	return nil
   387  }
   388  
   389  func (router *router) delete(w http.ResponseWriter, r *http.Request) {
   390  	req, err := readDeleteRequest(r)
   391  	if err != nil {
   392  
   393  		xhttp.WriteBadRequest(r.Context(), w, fmt.Errorf("failed to read delete request: %s", err))
   394  		return
   395  	}
   396  	err = router.s.Delete(r.Context(), req.ID)
   397  	if err != nil {
   398  		xhttp.WriteError(r.Context(), w, err)
   399  		return
   400  	}
   401  	err = writeDeleteResponse(w)
   402  	if err != nil {
   403  		log.Println(fmt.Errorf("failed to write delete response: %s", err))
   404  		return
   405  	}
   406  }
   407  
   408  // swagger:parameters deleteRepeatingEvent
   409  type deleteRequest struct {
   410  
   411  	// in:query
   412  	ID string `json:"id"`
   413  }
   414  
   415  // swagger:response deleteRepeatingEventResponse
   416  type deleteResponse struct{}
   417  
   418  func readDeleteRequest(r *http.Request) (*deleteRequest, error) {
   419  	id := chi.URLParam(r, "id")
   420  	return &deleteRequest{id}, nil
   421  }
   422  
   423  func writeDeleteResponse(w http.ResponseWriter) error {
   424  	w.Write([]byte{})
   425  	return nil
   426  }
   427  
   428  func (router *router) generate(w http.ResponseWriter, r *http.Request) {
   429  	req, err := readGenerateRequest(r)
   430  	if err != nil {
   431  		xhttp.WriteBadRequest(r.Context(), w, fmt.Errorf("failed to generate events: %s", err))
   432  		return
   433  	}
   434  	events, err := router.s.GenerateEvents(r.Context(), req.ID, req.Start, req.End)
   435  	if xerror.IsBadInputError(err) {
   436  		xhttp.WriteBadRequest(r.Context(), w, err)
   437  		return
   438  	}
   439  	if err != nil {
   440  		xhttp.WriteInternalError(r.Context(), w, fmt.Errorf("failed to generate events: %s", err))
   441  		return
   442  	}
   443  	err = writeGenerateResponse(w, &generateResponse{events})
   444  	if err != nil {
   445  		log.Println(fmt.Errorf("failed to write find response: %s", err))
   446  		return
   447  	}
   448  }
   449  
   450  // swagger:parameters generateRepeatingEvent
   451  type generateRequest struct {
   452  	// in:query
   453  	ID string `json:"id"`
   454  
   455  	// in:query
   456  	Start time.Time `json:"start"`
   457  
   458  	// in:query
   459  	End time.Time `json:"end"`
   460  }
   461  
   462  // swagger:response generateRepeatingEventResponse
   463  type generateResponse struct {
   464  
   465  	// in:body
   466  	Events []*event.Event
   467  }
   468  
   469  func readGenerateRequest(r *http.Request) (*generateRequest, error) {
   470  	id := chi.URLParam(r, "id")
   471  	startRaw := r.URL.Query().Get("start")
   472  	start, err := time.Parse(time.RFC3339, startRaw)
   473  	if err != nil {
   474  		return nil, fmt.Errorf("parse start time: %s", err)
   475  	}
   476  	endRaw := r.URL.Query().Get("end")
   477  	end, err := time.Parse(time.RFC3339, endRaw)
   478  	if err != nil {
   479  		return nil, fmt.Errorf("parse end time: %s", err)
   480  	}
   481  	return &generateRequest{ID: id, Start: start, End: end}, nil
   482  }
   483  
   484  func writeGenerateResponse(w http.ResponseWriter, resp *generateResponse) error {
   485  	data, err := json.Marshal(resp.Events)
   486  	if err != nil {
   487  		return err
   488  	}
   489  	w.Write(data)
   490  	return nil
   491  }
   492  
   493  func decodeRepeatingEvent(data []byte) (*RepeatingEvent, error) {
   494  	RepeatingEvent := &RepeatingEvent{}
   495  	err := json.Unmarshal(data, RepeatingEvent)
   496  	return RepeatingEvent, err
   497  }
   498  
   499  func encodeRepeatingEvent(RepeatingEvent *RepeatingEvent) ([]byte, error) {
   500  	return json.Marshal(RepeatingEvent)
   501  }
   502  
   503  func encodeRepeatingEvents(RepeatingEvents []RepeatingEvent) ([]byte, error) {
   504  	return json.Marshal(RepeatingEvents)
   505  }
   506  
   507  func decodeNewRepeatingEvent(data []byte) (*NewRepeatingEvent, error) {
   508  	RepeatingEvent := &NewRepeatingEvent{}
   509  	err := json.Unmarshal(data, RepeatingEvent)
   510  	return RepeatingEvent, err
   511  }