From c684a121552edf94968c89fd74f906b9ea1f5535 Mon Sep 17 00:00:00 2001 From: "n.nakhostin" Date: Tue, 2 Sep 2025 11:03:38 +0330 Subject: [PATCH] Update API Documentation and Enhance Tender Query Parameters - Changed references in API documentation from `main.HealthResponse` to `bootstrap.HealthResponse` for consistency. - Added new query parameters `publication_from` and `publication_to` to the tender listing and recommendation endpoints to allow filtering by publication date. - Updated the `ListTendersRequest` struct to include optional fields for `from` and `to` timestamps. - Modified the `TenderRepository` and `TenderService` interfaces to support the new filtering parameters in the tender listing functionality. - Enhanced the service and handler layers to process the new query parameters, improving the flexibility of tender retrieval. --- cmd/web/docs/docs.go | 88 ++++++++++++++++++++++------------- cmd/web/docs/swagger.json | 88 ++++++++++++++++++++++------------- cmd/web/docs/swagger.yaml | 66 ++++++++++++++++---------- internal/tender/form.go | 2 + internal/tender/handler.go | 39 +++++++++++++--- internal/tender/repository.go | 12 ++++- internal/tender/service.go | 10 ++-- 7 files changed, 203 insertions(+), 102 deletions(-) diff --git a/cmd/web/docs/docs.go b/cmd/web/docs/docs.go index 20e9565..9aa9e48 100644 --- a/cmd/web/docs/docs.go +++ b/cmd/web/docs/docs.go @@ -3428,13 +3428,13 @@ const docTemplate = `{ "200": { "description": "System is healthy and operational", "schema": { - "$ref": "#/definitions/main.HealthResponse" + "$ref": "#/definitions/bootstrap.HealthResponse" } }, "503": { "description": "System is unhealthy or experiencing issues", "schema": { - "$ref": "#/definitions/main.HealthResponse" + "$ref": "#/definitions/bootstrap.HealthResponse" } } } @@ -6368,6 +6368,18 @@ const docTemplate = `{ "description": "Maximum estimated value", "name": "max_estimated_value", "in": "query" + }, + { + "type": "number", + "description": "Publication from", + "name": "publication_from", + "in": "query" + }, + { + "type": "number", + "description": "Publication to", + "name": "publication_to", + "in": "query" } ], "responses": { @@ -6509,6 +6521,18 @@ const docTemplate = `{ "description": "Maximum estimated value", "name": "max_estimated_value", "in": "query" + }, + { + "type": "number", + "description": "Publication from", + "name": "publication_from", + "in": "query" + }, + { + "type": "number", + "description": "Publication to", + "name": "publication_to", + "in": "query" } ], "responses": { @@ -6548,6 +6572,36 @@ const docTemplate = `{ } }, "definitions": { + "bootstrap.HealthResponse": { + "type": "object", + "properties": { + "service": { + "description": "Service name", + "type": "string", + "example": "tender-management-api" + }, + "status": { + "description": "Current health status", + "type": "string", + "example": "healthy" + }, + "time": { + "description": "Current Unix timestamp", + "type": "integer", + "example": 1699123456 + }, + "uptime": { + "description": "Service uptime duration", + "type": "string", + "example": "24h30m15s" + }, + "version": { + "description": "API version", + "type": "string", + "example": "2.0.0" + } + } + }, "company.AddTagsForm": { "type": "object", "properties": { @@ -7790,36 +7844,6 @@ const docTemplate = `{ } } }, - "main.HealthResponse": { - "type": "object", - "properties": { - "service": { - "description": "Service name", - "type": "string", - "example": "tender-management-api" - }, - "status": { - "description": "Current health status", - "type": "string", - "example": "healthy" - }, - "time": { - "description": "Current Unix timestamp", - "type": "integer", - "example": 1699123456 - }, - "uptime": { - "description": "Service uptime duration", - "type": "string", - "example": "24h30m15s" - }, - "version": { - "description": "API version", - "type": "string", - "example": "2.0.0" - } - } - }, "response.APIError": { "type": "object", "properties": { diff --git a/cmd/web/docs/swagger.json b/cmd/web/docs/swagger.json index 514cfe3..615d889 100644 --- a/cmd/web/docs/swagger.json +++ b/cmd/web/docs/swagger.json @@ -3422,13 +3422,13 @@ "200": { "description": "System is healthy and operational", "schema": { - "$ref": "#/definitions/main.HealthResponse" + "$ref": "#/definitions/bootstrap.HealthResponse" } }, "503": { "description": "System is unhealthy or experiencing issues", "schema": { - "$ref": "#/definitions/main.HealthResponse" + "$ref": "#/definitions/bootstrap.HealthResponse" } } } @@ -6362,6 +6362,18 @@ "description": "Maximum estimated value", "name": "max_estimated_value", "in": "query" + }, + { + "type": "number", + "description": "Publication from", + "name": "publication_from", + "in": "query" + }, + { + "type": "number", + "description": "Publication to", + "name": "publication_to", + "in": "query" } ], "responses": { @@ -6503,6 +6515,18 @@ "description": "Maximum estimated value", "name": "max_estimated_value", "in": "query" + }, + { + "type": "number", + "description": "Publication from", + "name": "publication_from", + "in": "query" + }, + { + "type": "number", + "description": "Publication to", + "name": "publication_to", + "in": "query" } ], "responses": { @@ -6542,6 +6566,36 @@ } }, "definitions": { + "bootstrap.HealthResponse": { + "type": "object", + "properties": { + "service": { + "description": "Service name", + "type": "string", + "example": "tender-management-api" + }, + "status": { + "description": "Current health status", + "type": "string", + "example": "healthy" + }, + "time": { + "description": "Current Unix timestamp", + "type": "integer", + "example": 1699123456 + }, + "uptime": { + "description": "Service uptime duration", + "type": "string", + "example": "24h30m15s" + }, + "version": { + "description": "API version", + "type": "string", + "example": "2.0.0" + } + } + }, "company.AddTagsForm": { "type": "object", "properties": { @@ -7784,36 +7838,6 @@ } } }, - "main.HealthResponse": { - "type": "object", - "properties": { - "service": { - "description": "Service name", - "type": "string", - "example": "tender-management-api" - }, - "status": { - "description": "Current health status", - "type": "string", - "example": "healthy" - }, - "time": { - "description": "Current Unix timestamp", - "type": "integer", - "example": 1699123456 - }, - "uptime": { - "description": "Service uptime duration", - "type": "string", - "example": "24h30m15s" - }, - "version": { - "description": "API version", - "type": "string", - "example": "2.0.0" - } - } - }, "response.APIError": { "type": "object", "properties": { diff --git a/cmd/web/docs/swagger.yaml b/cmd/web/docs/swagger.yaml index fe03e5c..eacff34 100644 --- a/cmd/web/docs/swagger.yaml +++ b/cmd/web/docs/swagger.yaml @@ -1,5 +1,28 @@ basePath: / definitions: + bootstrap.HealthResponse: + properties: + service: + description: Service name + example: tender-management-api + type: string + status: + description: Current health status + example: healthy + type: string + time: + description: Current Unix timestamp + example: 1699123456 + type: integer + uptime: + description: Service uptime duration + example: 24h30m15s + type: string + version: + description: API version + example: 2.0.0 + type: string + type: object company.AddTagsForm: properties: categories: @@ -810,29 +833,6 @@ definitions: - feedback_type - tender_id type: object - main.HealthResponse: - properties: - service: - description: Service name - example: tender-management-api - type: string - status: - description: Current health status - example: healthy - type: string - time: - description: Current Unix timestamp - example: 1699123456 - type: integer - uptime: - description: Service uptime duration - example: 24h30m15s - type: string - version: - description: API version - example: 2.0.0 - type: string - type: object response.APIError: properties: code: @@ -3469,11 +3469,11 @@ paths: "200": description: System is healthy and operational schema: - $ref: '#/definitions/main.HealthResponse' + $ref: '#/definitions/bootstrap.HealthResponse' "503": description: System is unhealthy or experiencing issues schema: - $ref: '#/definitions/main.HealthResponse' + $ref: '#/definitions/bootstrap.HealthResponse' summary: Health check endpoint tags: - Admin-Health @@ -5284,6 +5284,14 @@ paths: in: query name: max_estimated_value type: number + - description: Publication from + in: query + name: publication_from + type: number + - description: Publication to + in: query + name: publication_to + type: number produces: - application/json responses: @@ -5371,6 +5379,14 @@ paths: in: query name: max_estimated_value type: number + - description: Publication from + in: query + name: publication_from + type: number + - description: Publication to + in: query + name: publication_to + type: number produces: - application/json responses: diff --git a/internal/tender/form.go b/internal/tender/form.go index 476c2c6..2e294b5 100644 --- a/internal/tender/form.go +++ b/internal/tender/form.go @@ -19,6 +19,8 @@ type ListTendersRequest struct { Limit int `json:"limit" valid:"range(1|100)~Limit must be between 1 and 100"` Offset int `json:"offset" valid:"range(0|999999)~Offset must be non-negative"` CompanyID *string `json:"company_id,omitempty"` // Optional company ID for match percentage calculation + From *int64 `json:"from,omitempty"` + To *int64 `json:"to,omitempty"` } // ListTendersResponse represents the response for listing tenders diff --git a/internal/tender/handler.go b/internal/tender/handler.go index f8804b4..678c55f 100644 --- a/internal/tender/handler.go +++ b/internal/tender/handler.go @@ -229,6 +229,8 @@ func (h *TenderHandler) ListTenders(c echo.Context) error { // @Param country_code query string false "Filter by country code" // @Param min_estimated_value query number false "Minimum estimated value" // @Param max_estimated_value query number false "Maximum estimated value" +// @Param publication_from query number false "Publication from" +// @Param publication_to query number false "Publication to" // @Success 200 {object} response.APIResponse{data=map[string]interface{}} // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse @@ -270,14 +272,21 @@ func (h *TenderHandler) GetPublicTenders(c echo.Context) error { companyID = &customerCompanyID } - req := ListTendersRequest{ - Criteria: criteria, - Limit: limit, - Offset: offset, - CompanyID: companyID, + from := int64(0) + if fromParam := c.QueryParam("publication_from"); fromParam != "" { + if parsed, err := strconv.ParseInt(fromParam, 10, 64); err == nil { + from = parsed + } } - result, err := h.service.ListTenders(c.Request().Context(), req) + to := int64(0) + if toParam := c.QueryParam("publication_to"); toParam != "" { + if parsed, err := strconv.ParseInt(toParam, 10, 64); err == nil { + to = parsed + } + } + + result, err := h.service.RecommendTenders(c.Request().Context(), companyID, &from, &to, limit, offset) if err != nil { return response.InternalServerError(c, "Failed to retrieve tenders") } @@ -295,6 +304,8 @@ func (h *TenderHandler) GetPublicTenders(c echo.Context) error { // @Param country_code query string false "Filter by country code" // @Param min_estimated_value query number false "Minimum estimated value" // @Param max_estimated_value query number false "Maximum estimated value" +// @Param publication_from query number false "Publication from" +// @Param publication_to query number false "Publication to" // @Success 200 {object} response.APIResponse{data=map[string]interface{}} // @Failure 400 {object} response.APIResponse // @Failure 500 {object} response.APIResponse @@ -336,7 +347,21 @@ func (h *TenderHandler) RecommendTenders(c echo.Context) error { companyID = &customerCompanyID } - result, err := h.service.RecommendTenders(c.Request().Context(), companyID, limit, offset) + from := int64(0) + if fromParam := c.QueryParam("publication_from"); fromParam != "" { + if parsed, err := strconv.ParseInt(fromParam, 10, 64); err == nil { + from = parsed + } + } + + to := int64(0) + if toParam := c.QueryParam("publication_to"); toParam != "" { + if parsed, err := strconv.ParseInt(toParam, 10, 64); err == nil { + to = parsed + } + } + + result, err := h.service.RecommendTenders(c.Request().Context(), companyID, &from, &to, limit, offset) if err != nil { return response.InternalServerError(c, "Failed to retrieve tenders") } diff --git a/internal/tender/repository.go b/internal/tender/repository.go index 119fd11..f801907 100644 --- a/internal/tender/repository.go +++ b/internal/tender/repository.go @@ -19,7 +19,7 @@ type TenderRepository interface { GetByNoticePublicationID(ctx context.Context, noticePublicationID string) (*Tender, error) Update(ctx context.Context, tender *Tender) error Delete(ctx context.Context, id string) error - List(ctx context.Context, criteria TenderSearchCriteria, limit, offset int) ([]Tender, int64, error) + List(ctx context.Context, criteria TenderSearchCriteria, limit, offset int, from *int64, to *int64) ([]Tender, int64, error) // Bulk operations CreateBatch(ctx context.Context, tenders []Tender) error @@ -214,7 +214,7 @@ func (r *tenderRepository) Delete(ctx context.Context, id string) error { } // List retrieves tenders with pagination and filtering -func (r *tenderRepository) List(ctx context.Context, criteria TenderSearchCriteria, limit, offset int) ([]Tender, int64, error) { +func (r *tenderRepository) List(ctx context.Context, criteria TenderSearchCriteria, limit, offset int, from *int64, to *int64) ([]Tender, int64, error) { filter := r.buildSearchFilter(criteria) pagination := mongopkg.Pagination{ @@ -224,6 +224,14 @@ func (r *tenderRepository) List(ctx context.Context, criteria TenderSearchCriter SortOrder: -1, } + if from != nil { + filter["publication_date"] = bson.M{"$gte": *from} + } + + if to != nil { + filter["publication_date"] = bson.M{"$lte": *to} + } + result, err := r.ormRepo.FindAll(ctx, filter, pagination) if err != nil { r.logger.Error("Failed to list tenders", map[string]interface{}{ diff --git a/internal/tender/service.go b/internal/tender/service.go index 6111006..82b069e 100644 --- a/internal/tender/service.go +++ b/internal/tender/service.go @@ -17,7 +17,7 @@ type TenderService interface { UpdateTender(ctx context.Context, req UpdateTenderRequest) (*Tender, error) DeleteTender(ctx context.Context, id string) error ListTenders(ctx context.Context, req ListTendersRequest) (*ListTendersResponse, error) - RecommendTenders(ctx context.Context, companyID *string, limit, offset int) (*ListTendersResponse, error) + RecommendTenders(ctx context.Context, companyID *string, from *int64, to *int64, limit, offset int) (*ListTendersResponse, error) // Maintenance operations UpdateExpiredTenders(ctx context.Context) error @@ -136,6 +136,8 @@ func (s *tenderService) ListTenders(ctx context.Context, req ListTendersRequest) "limit": req.Limit, "offset": req.Offset, "company_id": req.CompanyID, + "from": req.From, + "to": req.To, }) if req.Limit <= 0 { @@ -145,7 +147,7 @@ func (s *tenderService) ListTenders(ctx context.Context, req ListTendersRequest) req.Limit = 100 } - tenders, total, err := s.repository.List(ctx, req.Criteria, req.Limit, req.Offset) + tenders, total, err := s.repository.List(ctx, req.Criteria, req.Limit, req.Offset, req.From, req.To) if err != nil { s.logger.Error("Failed to list tenders", map[string]interface{}{ "error": err.Error(), @@ -220,7 +222,7 @@ func (s *tenderService) ListTenders(ctx context.Context, req ListTendersRequest) } // ListTenders retrieves tenders with pagination and filtering -func (s *tenderService) RecommendTenders(ctx context.Context, companyID *string, limit, offset int) (*ListTendersResponse, error) { +func (s *tenderService) RecommendTenders(ctx context.Context, companyID *string, from *int64, to *int64, limit, offset int) (*ListTendersResponse, error) { s.logger.Info("Recommend tenders", map[string]interface{}{ "company_id": companyID, }) @@ -244,7 +246,7 @@ func (s *tenderService) RecommendTenders(ctx context.Context, companyID *string, tenders, total, err := s.repository.List(ctx, TenderSearchCriteria{ Status: []TenderStatus{TenderStatusActive}, Classifications: company.Tags.CPVCodes, - }, limit, offset) + }, limit, offset, from, to) if err != nil { s.logger.Error("Failed to list tenders", map[string]interface{}{ "error": err.Error(),