From 283633460bb681b33f26183f98db832e7edbc93b Mon Sep 17 00:00:00 2001 From: ALI OMAR Ismail Date: Tue, 31 Dec 2024 19:53:49 +0100 Subject: [PATCH] Add SearchByInterval endpoint to query situation history --- internals/handlers/search_handlers.go | 51 +++++++++++++++++++ internals/history/service.go | 20 ++++++++ .../history/situation_history_builder.go | 35 +++++++++++++ internals/router/routes.go | 1 + 4 files changed, 107 insertions(+) diff --git a/internals/handlers/search_handlers.go b/internals/handlers/search_handlers.go index 2daad84c..a68a94b2 100644 --- a/internals/handlers/search_handlers.go +++ b/internals/handlers/search_handlers.go @@ -223,3 +223,54 @@ func SearchLastByCustomInterval(w http.ResponseWriter, r *http.Request) { render.JSON(w, r, result) } + +// SearchByInterval godoc +// @Summary query situation history data +// @Description query situation history data +// @Tags Search +// @Accept json +// @Produce json +// @Param situationid query int false "situationid" +// @Param situationinstanceid query int false "situationinstanceid" +// @Param interval query string true "month | week | day | hour | minute" +// @Security Bearer +// @Success 200 {array} search.QueryResult "query result" +// @Failure 500 "internal server error" +// @Router /engine/search/byinterval [get] +func SearchByInterval(w http.ResponseWriter, r *http.Request) { + + options, apiError, err := baseSearchOptions(w, r) + if err != nil { + render.Error(w, r, apiError, err) + return + } + + userCtx, _ := GetUserFromContext(r) + if !userCtx.HasPermission(permissions.New(permissions.TypeSituation, strconv.FormatInt(options.SituationID, 10), permissions.ActionSearch)) { + render.Error(w, r, render.ErrAPISecurityNoPermissions, errors.New("missing permission")) + return + } + + interval := r.URL.Query().Get("interval") + if interval != "month" && interval != "week" && interval != "day" && interval != "hour" && interval != "minute" { + zap.L().Warn("Error on parsing interval", zap.String("interval", interval), zap.Error(err)) + render.Error(w, r, render.ErrAPIParsingDuration, fmt.Errorf("interval %s is not supported", interval)) + return + } + + historySituations, err := history.S().GetAllHistorySituationsIdsByStandardInterval(options, interval) + if err != nil { + render.Error(w, r, render.ErrAPIDBSelectFailed, err) + return + } + + historyFacts, historySituationFacts, err := history.S().GetHistoryFactsFromSituation(historySituations) + if err != nil { + render.Error(w, r, render.ErrAPIDBSelectFailed, err) + return + } + + result := history.ExtractHistoryDataSearch(historySituations, historySituationFacts, historyFacts) + + render.JSON(w, r, result) +} diff --git a/internals/history/service.go b/internals/history/service.go index 6cc37154..5e7eaa35 100644 --- a/internals/history/service.go +++ b/internals/history/service.go @@ -76,6 +76,26 @@ func (service HistoryService) GetHistorySituationsIdsByStandardInterval(options ) } +func (service HistoryService) GetAllHistorySituationsIdsByStandardInterval(options GetHistorySituationsOptions, interval string) ([]HistorySituationsV4, error) { + subQuery := service.HistorySituationsQuerier.Builder. + GetAllHistorySituationsIdsByStandardInterval(options, interval) + + _, _, err := subQuery.ToSql() + if err != nil { + return nil, err + } + + query := service.HistorySituationsQuerier.Builder.GetAllHistorySituationsDetails(subQuery) + // TODO remove + //queryString, interfacevalue, errr := query.ToSql() + ////show values + //zap.L().Info("f", zap.String("queryString", queryString), zap.Any("interfacevalue", interfacevalue), zap.Error(errr)) + + return service.HistorySituationsQuerier.Query( + query, + ) +} + func (service HistoryService) GetHistorySituationsIdsByCustomInterval(options GetHistorySituationsOptions, interval time.Duration, referenceDate time.Time) ([]HistorySituationsV4, error) { subQuery, subQueryArgs, err := service.HistorySituationsQuerier.Builder. GetHistorySituationsIdsByCustomInterval(options, interval, referenceDate). diff --git a/internals/history/situation_history_builder.go b/internals/history/situation_history_builder.go index 460badf6..ec2e0ff3 100644 --- a/internals/history/situation_history_builder.go +++ b/internals/history/situation_history_builder.go @@ -66,6 +66,17 @@ func (builder HistorySituationsBuilder) GetHistorySituationsIdsByStandardInterva OrderBy("situation_id", "situation_instance_id", "date_trunc('"+interval+"', ts) desc, ts desc") } +func (builder HistorySituationsBuilder) GetAllHistorySituationsIdsByStandardInterval(options GetHistorySituationsOptions, interval string) sq.SelectBuilder { + return builder.newStatement(). + Select(fmt.Sprintf("situation_id, situation_instance_id, date_trunc('%s', ts) AS interval_ts, id", interval)). + From("situation_history_v5"). + Where("situation_id = ?", options.SituationID). + Where("situation_instance_id = ?", options.SituationInstanceID). + //Where(fmt.Sprintf("date_trunc('%s', ts) = date_trunc('%s', CURRENT_DATE)", interval, interval)). //TODO remove + Where(fmt.Sprintf("ts >= date_trunc('%s', CURRENT_DATE)", interval)). + Where(fmt.Sprintf("ts < date_trunc('%s', CURRENT_DATE) + INTERVAL '1 %s'", interval, interval)). + OrderBy("situation_id, situation_instance_id, interval_ts DESC, ts DESC") +} func (builder HistorySituationsBuilder) GetHistorySituationsIdsByCustomInterval(options GetHistorySituationsOptions, interval time.Duration, referenceDate time.Time) sq.SelectBuilder { intervalSeconds := fmt.Sprintf("%d", int64(interval.Seconds())) referenceDateStr := referenceDate.Format("2006-01-02T15:04:05Z07:00") @@ -85,6 +96,30 @@ func (builder HistorySituationsBuilder) GetHistorySituationsDetails(subQueryIds Where("sh.id = any ("+subQueryIds+")", subQueryIdsArgs...) } +func (builder HistorySituationsBuilder) GetAllHistorySituationsDetails(subQuery sq.SelectBuilder) sq.SelectBuilder { + return builder.newStatement(). + Select(` + sh.id, + sh.situation_id, + sh.situation_instance_id, + sh.ts, + sh.parameters, + sh.expression_facts, + sh.metadatas, + s.name AS situation_name, + coalesce(si.name, '') AS situation_instance_name, + c.id AS calendar_id, + c.name AS calendar_name, + c.description AS calendar_description, + c.timezone AS calendar_timezone + `). + FromSelect(subQuery, "sub"). + LeftJoin("situation_definition_v1 s ON sub.situation_id = s.id"). + LeftJoin("situation_template_instances_v1 si on s.id = si.situation_id"). + LeftJoin("calendar_v1 c on c.id = COALESCE(si.calendar_id, s.calendar_id)"). + InnerJoin("situation_history_v5 sh on (s.id = sh.situation_id and (sh.situation_instance_id = si.id OR sh.situation_instance_id = 0))") +} + func (builder HistorySituationsBuilder) Insert(history HistorySituationsV4, parametersJSON []byte, expressionFactsJSON []byte, metadatasJSON []byte) sq.InsertBuilder { return builder.newStatement().Insert("situation_history_v5"). Columns("id", "situation_id", "situation_instance_id", "ts", "parameters", "expression_facts", "metadatas"). diff --git a/internals/router/routes.go b/internals/router/routes.go index c0de8939..e306e370 100644 --- a/internals/router/routes.go +++ b/internals/router/routes.go @@ -170,6 +170,7 @@ func engineRouter(services Services) http.Handler { r.Get("/search/last", handlers.SearchLast) r.Get("/search/last/byinterval", handlers.SearchLastByInterval) r.Get("/search/last/bycustominterval", handlers.SearchLastByCustomInterval) + r.Get("/search/byinterval", handlers.SearchByInterval) r.Post("/history/facts/today/result", handlers.GetFactResultForTodayByCriteria) r.Post("/history/facts/date/result", handlers.GetFactResultByDateCriteria)