Skip to content

Commit

Permalink
feat(forecasts): add a new route to get the today forecasts
Browse files Browse the repository at this point in the history
  • Loading branch information
Valentin REVERSAT committed Oct 27, 2023
1 parent b92815a commit fc055a0
Show file tree
Hide file tree
Showing 8 changed files with 372 additions and 0 deletions.
62 changes: 62 additions & 0 deletions docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,68 @@ const docTemplate = `{
}
}
},
"/forecasts/today": {
"get": {
"description": "Get the closing schedule for today",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Forecasts"
],
"summary": "Get the closing schedule for today",
"parameters": [
{
"type": "integer",
"format": "int",
"default": 10,
"description": "Set the limit of the queried results",
"name": "limit",
"in": "query",
"required": true
},
{
"type": "integer",
"format": "int",
"default": 0,
"description": "Set the offset of the queried results",
"name": "offset",
"in": "query",
"required": true
},
{
"type": "string",
"default": "UTC",
"description": "Timezone to format the date related fields (TZ identifier)",
"name": "Timezone",
"in": "header"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/domains.ForecastsResponse"
}
},
"400": {
"description": "Some params are missing and/or not properly formatted fror the requests",
"schema": {
"$ref": "#/definitions/domains.APIErrorResponse"
}
},
"500": {
"description": "An error occured on the server side",
"schema": {
"$ref": "#/definitions/domains.APIErrorResponse"
}
}
}
}
},
"/forecasts/{id}": {
"get": {
"description": "Fetch a forecast by his unique ID",
Expand Down
62 changes: 62 additions & 0 deletions docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,68 @@
}
}
},
"/forecasts/today": {
"get": {
"description": "Get the closing schedule for today",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Forecasts"
],
"summary": "Get the closing schedule for today",
"parameters": [
{
"type": "integer",
"format": "int",
"default": 10,
"description": "Set the limit of the queried results",
"name": "limit",
"in": "query",
"required": true
},
{
"type": "integer",
"format": "int",
"default": 0,
"description": "Set the offset of the queried results",
"name": "offset",
"in": "query",
"required": true
},
{
"type": "string",
"default": "UTC",
"description": "Timezone to format the date related fields (TZ identifier)",
"name": "Timezone",
"in": "header"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/domains.ForecastsResponse"
}
},
"400": {
"description": "Some params are missing and/or not properly formatted fror the requests",
"schema": {
"$ref": "#/definitions/domains.APIErrorResponse"
}
},
"500": {
"description": "An error occured on the server side",
"schema": {
"$ref": "#/definitions/domains.APIErrorResponse"
}
}
}
}
},
"/forecasts/{id}": {
"get": {
"description": "Fetch a forecast by his unique ID",
Expand Down
44 changes: 44 additions & 0 deletions docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,50 @@ paths:
summary: Refresh the forecasts with the ones from the OpenData API
tags:
- Forecasts
/forecasts/today:
get:
consumes:
- application/json
description: Get the closing schedule for today
parameters:
- default: 10
description: Set the limit of the queried results
format: int
in: query
name: limit
required: true
type: integer
- default: 0
description: Set the offset of the queried results
format: int
in: query
name: offset
required: true
type: integer
- default: UTC
description: Timezone to format the date related fields (TZ identifier)
in: header
name: Timezone
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/domains.ForecastsResponse'
"400":
description: Some params are missing and/or not properly formatted fror
the requests
schema:
$ref: '#/definitions/domains.APIErrorResponse'
"500":
description: An error occured on the server side
schema:
$ref: '#/definitions/domains.APIErrorResponse'
summary: Get the closing schedule for today
tags:
- Forecasts
/refresh/last:
get:
description: Get the last trace of refresh action on POST /forecasts/refresh
Expand Down
90 changes: 90 additions & 0 deletions internal/api/controllers/forecast_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,96 @@ func (fC *ForecastController) GetAllForecats() gin.HandlerFunc {
return gin.HandlerFunc(fn)
}

// GetTodayForecasts godoc
//
// @Summary Get the closing schedule for today
// @Description Get the closing schedule for today
// @Tags Forecasts
// @Accept json
// @Produce json
// @Success 200 {object} domains.ForecastsResponse{}
// @Failure 400 {object} domains.APIErrorResponse{} "Some params are missing and/or not properly formatted fror the requests"
// @Failure 500 {object} domains.APIErrorResponse{} "An error occured on the server side"
// @Param limit query int true "Set the limit of the queried results" Format(int) default(10)
// @Param offset query int true "Set the offset of the queried results" Format(int) default(0)
// @Param Timezone header string false "Timezone to format the date related fields (TZ identifier)" default(UTC)
// @Router /forecasts/today [get]
func (fC *ForecastController) GetTodayForecasts() gin.HandlerFunc {
fn := func(c *gin.Context) {

if hub := sentrygin.GetHubFromContext(c); hub != nil {
hub.Scope().SetTag("controller", "GetCurrentForecasts")
}

var forecasts domains.Forecasts
var totalItemCount int

location, locationErr := utils.GetTimezoneFromHeader(c)
limit, limitErr := utils.GetIntParams(c, "limit")
offset, offsetErr := utils.GetIntParams(c, "offset")

if limitErr != nil {
c.JSON(http.StatusBadRequest, domains.APIErrorResponse{Error: limitErr.Error()})
sentry.CaptureException(limitErr)
return
}

if offsetErr != nil {
c.JSON(http.StatusBadRequest, domains.APIErrorResponse{Error: offsetErr.Error()})
sentry.CaptureException(offsetErr)
return
}

if locationErr != nil {
c.JSON(http.StatusBadRequest, domains.APIErrorResponse{Error: locationErr.Error()})
sentry.CaptureException(locationErr)
return
}

if limit == 0 {
errMessage := "the limit param need to be greater or equal to 1"
c.JSON(http.StatusBadRequest, domains.APIErrorResponse{Error: errMessage})
sentry.CaptureException(fmt.Errorf(errMessage))
return
}

err := fC.ForecastUsecase.GetTodayForecasts(
c,
&forecasts,
offset,
limit,
location,
&totalItemCount,
)

if err != nil {
c.JSON(http.StatusInternalServerError, domains.APIErrorResponse{Error: err.Error()})
sentry.CaptureException(locationErr)
return
}

links := utils.ComputeMetadaLinks(
totalItemCount,
limit,
offset,
fmt.Sprintf("%s/%s", c.Request.URL.Path, c.Request.URL.RawQuery),
)

response := domains.ForecastsResponse{
Links: links,
Hits: totalItemCount,
Forecasts: forecasts,
Limit: limit,
Offset: offset,
Timezone: location.String(),
}

c.JSON(http.StatusOK, response)
}

return gin.HandlerFunc(fn)
}

// RefreshForecasts godoc
//
// @Summary Refresh the forecasts with the ones from the OpenData API
Expand Down
1 change: 1 addition & 0 deletions internal/api/routers/forecasts_router.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ func ForecastsRouter(timeout time.Duration, db mongo.Database, group *gin.Router
forecastGroup := group.Group("/forecasts")
forecastGroup.GET("", forecastController.GetAllForecats())
forecastGroup.GET(":id", forecastController.GetForecastByID())
forecastGroup.GET("/today", forecastController.GetTodayForecasts())
forecastGroup.POST("/refresh", forecastController.RefreshForecasts())
}
17 changes: 17 additions & 0 deletions internal/domains/forecast_domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ func (forecasts *Forecasts) ChangeLocations(location *time.Location) {

type ForecastRepository interface {
GetByID(ctx context.Context, id string, forecast *Forecast) error
GetAllBetweenTwoDates(
ctx context.Context,
offset int,
limit int,
from time.Time,
to time.Time,
forecasts *Forecasts,
totalItemCount *int,
) error
GetAllFiltered(
ctx context.Context,
location *time.Location,
Expand All @@ -121,6 +130,14 @@ type ForecastRepository interface {

type ForecastUsecase interface {
GetByID(ctx context.Context, id string, forecast *Forecast, location *time.Location) error
GetTodayForecasts(
ctx context.Context,
forecasts *Forecasts,
offset int,
limit int,
location *time.Location,
totalItemCount *int,
) error
GetAllFiltered(
ctx context.Context,
location *time.Location,
Expand Down
46 changes: 46 additions & 0 deletions internal/repositories/forecast_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,52 @@ func (fR *forecastRepository) GetByID(

}

func (fR *forecastRepository) GetAllBetweenTwoDates(
ctx context.Context,
offset int,
limit int,
from time.Time,
to time.Time,
forecasts *domains.Forecasts,
totalItemCount *int,
) error {
var mongoResponse domains.ForecastMongoResponse
mongoFilter := bson.D{}

mongoFilter = append(
mongoFilter,
bson.E{
Key: "circulation_closing_date",
Value: bson.D{{Key: "$gte", Value: from}},
},
)

mongoFilter = append(
mongoFilter,
bson.E{
Key: "circulation_closing_date",
Value: bson.D{{Key: "$lt", Value: to}},
},
)

cursor, err := computeMongoCursor(ctx, mongoFilter, fR.collection, limit, offset)
if err != nil {
return err
}

for cursor.Next(ctx) {
if err := cursor.Decode(&mongoResponse); err != nil {
logrus.Info(err.Error())
return err
}
}

*forecasts = mongoResponse.Results
*totalItemCount = mongoResponse.Count[0].ItemCount

return err
}

func (fR *forecastRepository) GetAllFiltered(
ctx context.Context,
location *time.Location,
Expand Down
Loading

0 comments on commit fc055a0

Please sign in to comment.