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.
This commit is contained in:
n.nakhostin
2025-09-02 11:03:38 +03:30
parent cbf45a812d
commit c684a12155
7 changed files with 203 additions and 102 deletions
+56 -32
View File
@@ -3428,13 +3428,13 @@ const docTemplate = `{
"200": { "200": {
"description": "System is healthy and operational", "description": "System is healthy and operational",
"schema": { "schema": {
"$ref": "#/definitions/main.HealthResponse" "$ref": "#/definitions/bootstrap.HealthResponse"
} }
}, },
"503": { "503": {
"description": "System is unhealthy or experiencing issues", "description": "System is unhealthy or experiencing issues",
"schema": { "schema": {
"$ref": "#/definitions/main.HealthResponse" "$ref": "#/definitions/bootstrap.HealthResponse"
} }
} }
} }
@@ -6368,6 +6368,18 @@ const docTemplate = `{
"description": "Maximum estimated value", "description": "Maximum estimated value",
"name": "max_estimated_value", "name": "max_estimated_value",
"in": "query" "in": "query"
},
{
"type": "number",
"description": "Publication from",
"name": "publication_from",
"in": "query"
},
{
"type": "number",
"description": "Publication to",
"name": "publication_to",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -6509,6 +6521,18 @@ const docTemplate = `{
"description": "Maximum estimated value", "description": "Maximum estimated value",
"name": "max_estimated_value", "name": "max_estimated_value",
"in": "query" "in": "query"
},
{
"type": "number",
"description": "Publication from",
"name": "publication_from",
"in": "query"
},
{
"type": "number",
"description": "Publication to",
"name": "publication_to",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -6548,6 +6572,36 @@ const docTemplate = `{
} }
}, },
"definitions": { "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": { "company.AddTagsForm": {
"type": "object", "type": "object",
"properties": { "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": { "response.APIError": {
"type": "object", "type": "object",
"properties": { "properties": {
+56 -32
View File
@@ -3422,13 +3422,13 @@
"200": { "200": {
"description": "System is healthy and operational", "description": "System is healthy and operational",
"schema": { "schema": {
"$ref": "#/definitions/main.HealthResponse" "$ref": "#/definitions/bootstrap.HealthResponse"
} }
}, },
"503": { "503": {
"description": "System is unhealthy or experiencing issues", "description": "System is unhealthy or experiencing issues",
"schema": { "schema": {
"$ref": "#/definitions/main.HealthResponse" "$ref": "#/definitions/bootstrap.HealthResponse"
} }
} }
} }
@@ -6362,6 +6362,18 @@
"description": "Maximum estimated value", "description": "Maximum estimated value",
"name": "max_estimated_value", "name": "max_estimated_value",
"in": "query" "in": "query"
},
{
"type": "number",
"description": "Publication from",
"name": "publication_from",
"in": "query"
},
{
"type": "number",
"description": "Publication to",
"name": "publication_to",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -6503,6 +6515,18 @@
"description": "Maximum estimated value", "description": "Maximum estimated value",
"name": "max_estimated_value", "name": "max_estimated_value",
"in": "query" "in": "query"
},
{
"type": "number",
"description": "Publication from",
"name": "publication_from",
"in": "query"
},
{
"type": "number",
"description": "Publication to",
"name": "publication_to",
"in": "query"
} }
], ],
"responses": { "responses": {
@@ -6542,6 +6566,36 @@
} }
}, },
"definitions": { "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": { "company.AddTagsForm": {
"type": "object", "type": "object",
"properties": { "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": { "response.APIError": {
"type": "object", "type": "object",
"properties": { "properties": {
+41 -25
View File
@@ -1,5 +1,28 @@
basePath: / basePath: /
definitions: 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: company.AddTagsForm:
properties: properties:
categories: categories:
@@ -810,29 +833,6 @@ definitions:
- feedback_type - feedback_type
- tender_id - tender_id
type: object 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: response.APIError:
properties: properties:
code: code:
@@ -3469,11 +3469,11 @@ paths:
"200": "200":
description: System is healthy and operational description: System is healthy and operational
schema: schema:
$ref: '#/definitions/main.HealthResponse' $ref: '#/definitions/bootstrap.HealthResponse'
"503": "503":
description: System is unhealthy or experiencing issues description: System is unhealthy or experiencing issues
schema: schema:
$ref: '#/definitions/main.HealthResponse' $ref: '#/definitions/bootstrap.HealthResponse'
summary: Health check endpoint summary: Health check endpoint
tags: tags:
- Admin-Health - Admin-Health
@@ -5284,6 +5284,14 @@ paths:
in: query in: query
name: max_estimated_value name: max_estimated_value
type: number type: number
- description: Publication from
in: query
name: publication_from
type: number
- description: Publication to
in: query
name: publication_to
type: number
produces: produces:
- application/json - application/json
responses: responses:
@@ -5371,6 +5379,14 @@ paths:
in: query in: query
name: max_estimated_value name: max_estimated_value
type: number type: number
- description: Publication from
in: query
name: publication_from
type: number
- description: Publication to
in: query
name: publication_to
type: number
produces: produces:
- application/json - application/json
responses: responses:
+2
View File
@@ -19,6 +19,8 @@ type ListTendersRequest struct {
Limit int `json:"limit" valid:"range(1|100)~Limit must be between 1 and 100"` 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"` 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 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 // ListTendersResponse represents the response for listing tenders
+32 -7
View File
@@ -229,6 +229,8 @@ func (h *TenderHandler) ListTenders(c echo.Context) error {
// @Param country_code query string false "Filter by country code" // @Param country_code query string false "Filter by country code"
// @Param min_estimated_value query number false "Minimum estimated value" // @Param min_estimated_value query number false "Minimum estimated value"
// @Param max_estimated_value query number false "Maximum 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{}} // @Success 200 {object} response.APIResponse{data=map[string]interface{}}
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
@@ -270,14 +272,21 @@ func (h *TenderHandler) GetPublicTenders(c echo.Context) error {
companyID = &customerCompanyID companyID = &customerCompanyID
} }
req := ListTendersRequest{ from := int64(0)
Criteria: criteria, if fromParam := c.QueryParam("publication_from"); fromParam != "" {
Limit: limit, if parsed, err := strconv.ParseInt(fromParam, 10, 64); err == nil {
Offset: offset, from = parsed
CompanyID: companyID, }
} }
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 { if err != nil {
return response.InternalServerError(c, "Failed to retrieve tenders") 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 country_code query string false "Filter by country code"
// @Param min_estimated_value query number false "Minimum estimated value" // @Param min_estimated_value query number false "Minimum estimated value"
// @Param max_estimated_value query number false "Maximum 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{}} // @Success 200 {object} response.APIResponse{data=map[string]interface{}}
// @Failure 400 {object} response.APIResponse // @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse // @Failure 500 {object} response.APIResponse
@@ -336,7 +347,21 @@ func (h *TenderHandler) RecommendTenders(c echo.Context) error {
companyID = &customerCompanyID 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 { if err != nil {
return response.InternalServerError(c, "Failed to retrieve tenders") return response.InternalServerError(c, "Failed to retrieve tenders")
} }
+10 -2
View File
@@ -19,7 +19,7 @@ type TenderRepository interface {
GetByNoticePublicationID(ctx context.Context, noticePublicationID string) (*Tender, error) GetByNoticePublicationID(ctx context.Context, noticePublicationID string) (*Tender, error)
Update(ctx context.Context, tender *Tender) error Update(ctx context.Context, tender *Tender) error
Delete(ctx context.Context, id string) 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 // Bulk operations
CreateBatch(ctx context.Context, tenders []Tender) error 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 // 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) filter := r.buildSearchFilter(criteria)
pagination := mongopkg.Pagination{ pagination := mongopkg.Pagination{
@@ -224,6 +224,14 @@ func (r *tenderRepository) List(ctx context.Context, criteria TenderSearchCriter
SortOrder: -1, 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) result, err := r.ormRepo.FindAll(ctx, filter, pagination)
if err != nil { if err != nil {
r.logger.Error("Failed to list tenders", map[string]interface{}{ r.logger.Error("Failed to list tenders", map[string]interface{}{
+6 -4
View File
@@ -17,7 +17,7 @@ type TenderService interface {
UpdateTender(ctx context.Context, req UpdateTenderRequest) (*Tender, error) UpdateTender(ctx context.Context, req UpdateTenderRequest) (*Tender, error)
DeleteTender(ctx context.Context, id string) error DeleteTender(ctx context.Context, id string) error
ListTenders(ctx context.Context, req ListTendersRequest) (*ListTendersResponse, 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 // Maintenance operations
UpdateExpiredTenders(ctx context.Context) error UpdateExpiredTenders(ctx context.Context) error
@@ -136,6 +136,8 @@ func (s *tenderService) ListTenders(ctx context.Context, req ListTendersRequest)
"limit": req.Limit, "limit": req.Limit,
"offset": req.Offset, "offset": req.Offset,
"company_id": req.CompanyID, "company_id": req.CompanyID,
"from": req.From,
"to": req.To,
}) })
if req.Limit <= 0 { if req.Limit <= 0 {
@@ -145,7 +147,7 @@ func (s *tenderService) ListTenders(ctx context.Context, req ListTendersRequest)
req.Limit = 100 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 { if err != nil {
s.logger.Error("Failed to list tenders", map[string]interface{}{ s.logger.Error("Failed to list tenders", map[string]interface{}{
"error": err.Error(), "error": err.Error(),
@@ -220,7 +222,7 @@ func (s *tenderService) ListTenders(ctx context.Context, req ListTendersRequest)
} }
// ListTenders retrieves tenders with pagination and filtering // 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{}{ s.logger.Info("Recommend tenders", map[string]interface{}{
"company_id": companyID, "company_id": companyID,
}) })
@@ -244,7 +246,7 @@ func (s *tenderService) RecommendTenders(ctx context.Context, companyID *string,
tenders, total, err := s.repository.List(ctx, TenderSearchCriteria{ tenders, total, err := s.repository.List(ctx, TenderSearchCriteria{
Status: []TenderStatus{TenderStatusActive}, Status: []TenderStatus{TenderStatusActive},
Classifications: company.Tags.CPVCodes, Classifications: company.Tags.CPVCodes,
}, limit, offset) }, limit, offset, from, to)
if err != nil { if err != nil {
s.logger.Error("Failed to list tenders", map[string]interface{}{ s.logger.Error("Failed to list tenders", map[string]interface{}{
"error": err.Error(), "error": err.Error(),