diff --git a/internal/apigw/apiv1/handlers_datastore.go b/internal/apigw/apiv1/handlers_datastore.go index ef8a47f5..a8436511 100644 --- a/internal/apigw/apiv1/handlers_datastore.go +++ b/internal/apigw/apiv1/handlers_datastore.go @@ -480,31 +480,8 @@ func (c *Client) RevokeDocument(ctx context.Context, req *RevokeDocumentRequest) return nil } -// SearchDocumentsRequest the request to search for documents -type SearchDocumentsRequest struct { - AuthenticSource string `json:"authentic_source,omitempty"` - DocumentType string `json:"document_type,omitempty"` - DocumentID string `json:"document_id,omitempty"` - CollectID string `json:"collect_id,omitempty"` - - AuthenticSourcePersonID string `json:"authentic_source_person_id,omitempty"` - FamilyName string `json:"family_name,omitempty"` - GivenName string `json:"given_name,omitempty"` - BirthDate string `json:"birth_date,omitempty"` - - Limit int64 `json:"limit,omitempty"` - Fields []string `json:"fields,omitempty"` - SortFields map[string]int `json:"sort_fields,omitempty"` -} - -// SearchDocumentsReply the reply from search documents -type SearchDocumentsReply struct { - Documents []*model.CompleteDocument `json:"documents"` - HasMoreResults bool `json:"has_more_results"` -} - // SearchDocuments search for documents -func (c *Client) SearchDocuments(ctx context.Context, req *SearchDocumentsRequest) (*SearchDocumentsReply, error) { +func (c *Client) SearchDocuments(ctx context.Context, req *model.SearchDocumentsRequest) (*model.SearchDocumentsReply, error) { docs, hasMore, err := c.db.VCDatastoreColl.SearchDocuments(ctx, &db.SearchDocumentsQuery{ AuthenticSource: req.AuthenticSource, DocumentType: req.DocumentType, @@ -520,7 +497,7 @@ func (c *Client) SearchDocuments(ctx context.Context, req *SearchDocumentsReques if err != nil { return nil, err } - resp := &SearchDocumentsReply{ + resp := &model.SearchDocumentsReply{ Documents: docs, HasMoreResults: hasMore, } diff --git a/internal/apigw/httpserver/api.go b/internal/apigw/httpserver/api.go index 6dd0bee1..c0e35782 100644 --- a/internal/apigw/httpserver/api.go +++ b/internal/apigw/httpserver/api.go @@ -25,7 +25,7 @@ type Apiv1 interface { GetConsent(ctx context.Context, req *apiv1.GetConsentRequest) (*model.Consent, error) // datastore endpoints - disabled in production - SearchDocuments(ctx context.Context, req *apiv1.SearchDocumentsRequest) (*apiv1.SearchDocumentsReply, error) + SearchDocuments(ctx context.Context, req *model.SearchDocumentsRequest) (*model.SearchDocumentsReply, error) // credential endpoints Revoke(ctx context.Context, req *apiv1.RevokeRequest) (*apiv1.RevokeReply, error) diff --git a/internal/apigw/httpserver/endpoints.go b/internal/apigw/httpserver/endpoints.go index 5470802f..9c1719db 100644 --- a/internal/apigw/httpserver/endpoints.go +++ b/internal/apigw/httpserver/endpoints.go @@ -4,6 +4,7 @@ import ( "context" "vc/internal/apigw/apiv1" "vc/internal/gen/status/apiv1_status" + "vc/pkg/model" "go.opentelemetry.io/otel/codes" @@ -106,7 +107,7 @@ func (s *Service) endpointSearchDocuments(ctx context.Context, c *gin.Context) ( ctx, span := s.tracer.Start(ctx, "httpserver:endpointSearchDocuments") defer span.End() - request := &apiv1.SearchDocumentsRequest{} + request := &model.SearchDocumentsRequest{} if err := s.httpHelpers.Binding.Request(ctx, c, request); err != nil { span.SetStatus(codes.Error, err.Error()) return nil, err diff --git a/internal/portal/apiv1/apigw_client.go b/internal/portal/apiv1/apigw_client.go index b2707761..2aaff90a 100644 --- a/internal/portal/apiv1/apigw_client.go +++ b/internal/portal/apiv1/apigw_client.go @@ -1,7 +1,6 @@ package apiv1 import ( - apiv1_apigw "vc/internal/apigw/apiv1" "vc/pkg/logger" "vc/pkg/model" "vc/pkg/trace" @@ -20,8 +19,8 @@ func NewAPIGWClient(cfg *model.Cfg, tracer *trace.Tracer, logger *logger.Log) *A } // SearchDocuments sends POST to /api/v1/document/search -func (c *APIGWClient) SearchDocuments(req *apiv1_apigw.SearchDocumentsRequest) (*apiv1_apigw.SearchDocumentsReply, error) { - reply, err := DoPostJSONGeneric[apiv1_apigw.SearchDocumentsReply](c.VCBaseClient, "/api/v1/document/search", req) +func (c *APIGWClient) SearchDocuments(req *model.SearchDocumentsRequest) (*model.SearchDocumentsReply, error) { + reply, err := DoPostJSONGeneric[model.SearchDocumentsReply](c.VCBaseClient, "/api/v1/document/search", req) if err != nil { return nil, err } diff --git a/internal/portal/apiv1/client.go b/internal/portal/apiv1/client.go index 4c0146a0..2ab51743 100644 --- a/internal/portal/apiv1/client.go +++ b/internal/portal/apiv1/client.go @@ -2,6 +2,7 @@ package apiv1 import ( "context" + "vc/pkg/datastoreclient" "vc/pkg/logger" "vc/pkg/model" "vc/pkg/trace" @@ -13,7 +14,8 @@ type Client struct { log *logger.Log tracer *trace.Tracer - apigwClient *APIGWClient + apigwClient *APIGWClient + datastoreClient *datastoreclient.Client } // New creates a new instance of the public api @@ -25,6 +27,12 @@ func New(ctx context.Context, tracer *trace.Tracer, cfg *model.Cfg, log *logger. apigwClient: NewAPIGWClient(cfg, tracer, log.New("apiwg_client")), } + var err error + c.datastoreClient, err = datastoreclient.New(&datastoreclient.Config{URL: cfg.Portal.Services.APIGW.BaseURL}) + if err != nil { + return nil, err + } + c.log.Info("Started") return c, nil diff --git a/internal/portal/apiv1/handlers.go b/internal/portal/apiv1/handlers.go index 2d203a39..00a2f36c 100644 --- a/internal/portal/apiv1/handlers.go +++ b/internal/portal/apiv1/handlers.go @@ -2,7 +2,8 @@ package apiv1 import ( "context" - apiv1_apigw "vc/internal/apigw/apiv1" + "errors" + "net/http" "vc/internal/gen/status/apiv1_status" "vc/pkg/model" ) @@ -17,10 +18,14 @@ func (c *Client) Status(ctx context.Context, req *apiv1_status.StatusRequest) (* } // SearchDocuments search for documents -func (c *Client) SearchDocuments(ctx context.Context, req *apiv1_apigw.SearchDocumentsRequest) (*apiv1_apigw.SearchDocumentsReply, error) { - reply, err := c.apigwClient.SearchDocuments(req) +func (c *Client) SearchDocuments(ctx context.Context, req *model.SearchDocumentsRequest) (*model.SearchDocumentsReply, error) { + //reply, err := c.apigwClient.SearchDocuments(req) + reply, httpResponse, err := c.datastoreClient.Document.Search(ctx, req) if err != nil { return nil, err } + if httpResponse.StatusCode != http.StatusOK { + return nil, errors.New(httpResponse.Status) + } return reply, nil } diff --git a/internal/portal/httpserver/api.go b/internal/portal/httpserver/api.go index d2d0beb9..3fdbd1a7 100644 --- a/internal/portal/httpserver/api.go +++ b/internal/portal/httpserver/api.go @@ -2,13 +2,13 @@ package httpserver import ( "context" - apiv1_apigw "vc/internal/apigw/apiv1" "vc/internal/gen/status/apiv1_status" + "vc/pkg/model" ) // Apiv1 interface type Apiv1 interface { Status(ctx context.Context, req *apiv1_status.StatusRequest) (*apiv1_status.StatusReply, error) - SearchDocuments(ctx context.Context, request *apiv1_apigw.SearchDocumentsRequest) (*apiv1_apigw.SearchDocumentsReply, error) + SearchDocuments(ctx context.Context, request *model.SearchDocumentsRequest) (*model.SearchDocumentsReply, error) } diff --git a/internal/portal/httpserver/endpoints.go b/internal/portal/httpserver/endpoints.go index 7f874a6f..5f3414d0 100644 --- a/internal/portal/httpserver/endpoints.go +++ b/internal/portal/httpserver/endpoints.go @@ -2,8 +2,8 @@ package httpserver import ( "context" - apiv1_apigw "vc/internal/apigw/apiv1" "vc/internal/gen/status/apiv1_status" + "vc/pkg/model" "github.com/gin-gonic/gin" ) @@ -18,7 +18,7 @@ func (s *Service) endpointHealth(ctx context.Context, c *gin.Context) (any, erro } func (s *Service) endpointSearchDocuments(ctx context.Context, c *gin.Context) (any, error) { - request := &apiv1_apigw.SearchDocumentsRequest{} + request := &model.SearchDocumentsRequest{} if err := s.httpHelpers.Binding.Request(ctx, c, request); err != nil { return nil, err } diff --git a/internal/ui/apiv1/apigw_client.go b/internal/ui/apiv1/apigw_client.go index 5bb4bc56..f13f5057 100644 --- a/internal/ui/apiv1/apigw_client.go +++ b/internal/ui/apiv1/apigw_client.go @@ -74,8 +74,8 @@ func (c *APIGWClient) Notification(req *NotificationRequest) (any, error) { } // SearchDocuments sends POST to /api/v1/document/search -func (c *APIGWClient) SearchDocuments(req *apiv1_apigw.SearchDocumentsRequest) (*apiv1_apigw.SearchDocumentsReply, error) { - reply, err := DoPostJSONGeneric[apiv1_apigw.SearchDocumentsReply](c.VCBaseClient, "/api/v1/document/search", req) +func (c *APIGWClient) SearchDocuments(req *model.SearchDocumentsRequest) (*model.SearchDocumentsReply, error) { + reply, err := DoPostJSONGeneric[model.SearchDocumentsReply](c.VCBaseClient, "/api/v1/document/search", req) if err != nil { return nil, err } diff --git a/internal/ui/apiv1/handlers.go b/internal/ui/apiv1/handlers.go index ecdf1ddf..9b687d9c 100644 --- a/internal/ui/apiv1/handlers.go +++ b/internal/ui/apiv1/handlers.go @@ -191,7 +191,7 @@ func (c *Client) DecodeCredential(ctx context.Context, req *apiv1_verifier.Decod return reply, nil } -func (c *Client) SearchDocuments(ctx context.Context, req *apiv1_apigw.SearchDocumentsRequest) (*apiv1_apigw.SearchDocumentsReply, error) { +func (c *Client) SearchDocuments(ctx context.Context, req *model.SearchDocumentsRequest) (*model.SearchDocumentsReply, error) { reply, err := c.apigwClient.SearchDocuments(req) if err != nil { return nil, err diff --git a/internal/ui/httpserver/api.go b/internal/ui/httpserver/api.go index 13e14ebd..61d991bf 100644 --- a/internal/ui/httpserver/api.go +++ b/internal/ui/httpserver/api.go @@ -7,6 +7,7 @@ import ( apiv1_mockas "vc/internal/mockas/apiv1" "vc/internal/ui/apiv1" apiv1_verifier "vc/internal/verifier/apiv1" + "vc/pkg/model" ) type Apiv1 interface { @@ -23,7 +24,7 @@ type Apiv1 interface { Credential(ctx context.Context, request *apiv1.CredentialRequest) (any, error) GetDocument(ctx context.Context, request *apiv1.GetDocumentRequest) (any, error) Notification(ctx context.Context, reguest *apiv1.NotificationRequest) (any, error) - SearchDocuments(ctx context.Context, request *apiv1_apigw.SearchDocumentsRequest) (*apiv1_apigw.SearchDocumentsReply, error) + SearchDocuments(ctx context.Context, request *model.SearchDocumentsRequest) (*model.SearchDocumentsReply, error) DeleteDocument(ctx context.Context, request *apiv1_apigw.DeleteDocumentRequest) error // mockas diff --git a/internal/ui/httpserver/endpoints.go b/internal/ui/httpserver/endpoints.go index f0220e45..f2f2cb00 100644 --- a/internal/ui/httpserver/endpoints.go +++ b/internal/ui/httpserver/endpoints.go @@ -9,6 +9,7 @@ import ( apiv1_mockas "vc/internal/mockas/apiv1" "vc/internal/ui/apiv1" apiv1_verifier "vc/internal/verifier/apiv1" + "vc/pkg/model" "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" @@ -218,7 +219,7 @@ func (s *Service) endpointDecodeCredential(ctx context.Context, c *gin.Context) } func (s *Service) endpointSearchDocuments(ctx context.Context, c *gin.Context) (any, error) { - request := &apiv1_apigw.SearchDocumentsRequest{} + request := &model.SearchDocumentsRequest{} if err := s.httpHelpers.Binding.Request(ctx, c, request); err != nil { return nil, err } diff --git a/pkg/datastoreclient/client.go b/pkg/datastoreclient/client.go index 29901358..f8b9aa6a 100644 --- a/pkg/datastoreclient/client.go +++ b/pkg/datastoreclient/client.go @@ -81,7 +81,7 @@ func (c *Client) newRequest(ctx context.Context, method, path string, body any) } // Do does the new request -func (c *Client) do(ctx context.Context, req *http.Request, reply any) (*http.Response, error) { +func (c *Client) do(ctx context.Context, req *http.Request, reply any, prefixReplyJsonWithData bool) (*http.Response, error) { resp, err := c.httpClient.Do(req) if err != nil { return nil, err @@ -100,18 +100,33 @@ func (c *Client) do(ctx context.Context, req *http.Request, reply any) (*http.Re return nil, err } - r := struct { - Data any `json:"data"` - }{ - Data: reply, + var r any + if prefixReplyJsonWithData { + r = &struct { + Data any `json:"data"` + }{ + Data: reply, + } + } else { + r = &reply } - if err := json.NewDecoder(resp.Body).Decode(&r); err != nil { + if err := json.NewDecoder(resp.Body).Decode(r); err != nil { c.log.Error(err, "failed to decode response") return nil, err } return resp, nil + +} + +// read body and make it reusable +func readBody(body io.ReadCloser) ([]byte, error) { + buf := &bytes.Buffer{} + if _, err := buf.ReadFrom(body); err != nil { + return nil, err + } + return buf.Bytes(), nil } func checkResponse(r *http.Response) error { @@ -127,7 +142,7 @@ func checkResponse(r *http.Response) error { return ErrInvalidRequest } -func (c *Client) call(ctx context.Context, method, url string, body, reply any) (*http.Response, error) { +func (c *Client) call(ctx context.Context, method, url string, body, reply any, prefixReplyJsonWithData bool) (*http.Response, error) { request, err := c.newRequest( ctx, method, @@ -138,7 +153,7 @@ func (c *Client) call(ctx context.Context, method, url string, body, reply any) return nil, err } - resp, err := c.do(ctx, request, reply) + resp, err := c.do(ctx, request, reply, prefixReplyJsonWithData) if err != nil { return resp, err } diff --git a/pkg/datastoreclient/endpoints_document.go b/pkg/datastoreclient/endpoints_document.go index 69d4f075..369e094f 100644 --- a/pkg/datastoreclient/endpoints_document.go +++ b/pkg/datastoreclient/endpoints_document.go @@ -25,7 +25,7 @@ type DocumentGetQuery struct { func (s *documentHandler) Get(ctx context.Context, query *DocumentGetQuery) (*model.Document, *http.Response, error) { url := fmt.Sprintf("%s", s.service) reply := &model.Document{} - resp, err := s.client.call(ctx, http.MethodPost, url, nil, reply) + resp, err := s.client.call(ctx, http.MethodPost, url, nil, reply, true) if err != nil { return nil, resp, err } @@ -46,7 +46,7 @@ func (s *documentHandler) List(ctx context.Context, query *DocumentListQuery) ([ url := fmt.Sprintf("%s/%s", s.service, "list") reply := []model.DocumentList{} - resp, err := s.client.call(ctx, http.MethodPost, url, nil, reply) + resp, err := s.client.call(ctx, http.MethodPost, url, nil, reply, true) if err != nil { return nil, resp, err } @@ -71,7 +71,21 @@ func (s *documentHandler) CollectID(ctx context.Context, query *DocumentCollectI url := fmt.Sprintf("%s/%s", s.service, "collect_id") reply := &model.Document{} - resp, err := s.client.call(ctx, http.MethodPost, url, query, reply) + resp, err := s.client.call(ctx, http.MethodPost, url, query, reply, true) + if err != nil { + return nil, resp, err + } + return reply, resp, nil +} + +func (s *documentHandler) Search(ctx context.Context, query *model.SearchDocumentsRequest) (*model.SearchDocumentsReply, *http.Response, error) { + s.log.Debug("Search (Documents)") + + url := fmt.Sprintf("%s/%s", s.service, "search") + reply := &model.SearchDocumentsReply{ + Documents: []*model.CompleteDocument{}, + } + resp, err := s.client.call(ctx, http.MethodPost, url, query, reply, false) if err != nil { return nil, resp, err } diff --git a/pkg/datastoreclient/endpoints_identity.go b/pkg/datastoreclient/endpoints_identity.go index cf981b3b..e684ba23 100644 --- a/pkg/datastoreclient/endpoints_identity.go +++ b/pkg/datastoreclient/endpoints_identity.go @@ -26,7 +26,7 @@ func (s *identityHandler) Mapping(ctx context.Context, query *IdentityMappingQue url := fmt.Sprintf("%s/%s", s.service, "mapping") reply := "" - resp, err := s.client.call(ctx, http.MethodPost, url, nil, reply) + resp, err := s.client.call(ctx, http.MethodPost, url, nil, reply, true) if err != nil { return "", resp, err } diff --git a/pkg/model/generic.go b/pkg/model/generic.go index 67d46dc5..41c0d769 100644 --- a/pkg/model/generic.go +++ b/pkg/model/generic.go @@ -384,3 +384,26 @@ type QR struct { // required: true CredentialOfferURL string `json:"credential_offer,omitempty" bson:"credential_offer"` } + +// SearchDocumentsReply the reply from search documents +type SearchDocumentsReply struct { + Documents []*CompleteDocument `json:"documents"` + HasMoreResults bool `json:"has_more_results"` +} + +// SearchDocumentsRequest the request to search for documents +type SearchDocumentsRequest struct { + AuthenticSource string `json:"authentic_source,omitempty"` + DocumentType string `json:"document_type,omitempty"` + DocumentID string `json:"document_id,omitempty"` + CollectID string `json:"collect_id,omitempty"` + + AuthenticSourcePersonID string `json:"authentic_source_person_id,omitempty"` + FamilyName string `json:"family_name,omitempty"` + GivenName string `json:"given_name,omitempty"` + BirthDate string `json:"birth_date,omitempty"` + + Limit int64 `json:"limit,omitempty"` + Fields []string `json:"fields,omitempty"` + SortFields map[string]int `json:"sort_fields,omitempty"` +}