Merged in feature/docresult (pull request #105)

Document Result Endpoint

* testing

* cleantesting

* progress

* query

* lint

* readme

* dockerclient

* api

* passtest

* tests

* test
This commit is contained in:
Michael McGuinness
2025-03-17 18:14:15 +00:00
parent f7c41c4ef3
commit 555b6d420b
105 changed files with 3276 additions and 2484 deletions
+843
View File
@@ -0,0 +1,843 @@
// Package queryapi provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version 2.4.1 DO NOT EDIT.
package queryapi
import (
"bytes"
"compress/gzip"
"encoding/base64"
"fmt"
"net/http"
"net/url"
"path"
"strings"
"time"
"github.com/getkin/kin-openapi/openapi3"
"github.com/labstack/echo/v4"
"github.com/oapi-codegen/runtime"
openapi_types "github.com/oapi-codegen/runtime/types"
)
const (
PlaceholderAuthScopes = "placeholderAuth.Scopes"
)
// Defines values for ClientStatus.
const (
INSYNC ClientStatus = "IN_SYNC"
NOTSYNCED ClientStatus = "NOT_SYNCED"
NOTSYNCING ClientStatus = "NOT_SYNCING"
)
// Defines values for ExportStatus.
const (
Completed ExportStatus = "completed"
Failed ExportStatus = "failed"
InProgress ExportStatus = "in_progress"
)
// Defines values for FieldFilterCondition.
const (
ClosedInterval FieldFilterCondition = "closed_interval"
Exclude FieldFilterCondition = "exclude"
GreaterThan FieldFilterCondition = "greater_than"
Include FieldFilterCondition = "include"
LeftClosedInterval FieldFilterCondition = "left_closed_interval"
LessThan FieldFilterCondition = "less_than"
OpenInterval FieldFilterCondition = "open_interval"
RightClosedInterval FieldFilterCondition = "right_closed_interval"
)
// Defines values for QueryType.
const (
CONTEXTFULL QueryType = "CONTEXT_FULL"
JSONEXTRACTOR QueryType = "JSON_EXTRACTOR"
)
// ClientCanSync If the client is allowing active syncs
type ClientCanSync = bool
// ClientCreate The properties for creation.
type ClientCreate struct {
// Id The client external id
Id ClientID `json:"id"`
// Name The client name
Name ClientName `json:"name"`
}
// ClientID The client external id
type ClientID = string
// ClientIDBody The client id.
type ClientIDBody struct {
// Id The client external id
Id ClientID `json:"id"`
}
// ClientName The client name
type ClientName = string
// ClientStatus Specifies the status of a client.
type ClientStatus string
// ClientStatusBody A client status information object.
type ClientStatusBody struct {
// Status Specifies the status of a client.
Status ClientStatus `json:"status"`
}
// ClientUID The client internal unique id
type ClientUID = openapi_types.UUID
// ClientUpdate The properties that may be updated.
type ClientUpdate struct {
// CanSync If the client is allowing active syncs
CanSync *ClientCanSync `json:"can_sync,omitempty"`
// Name The client name
Name *ClientName `json:"name,omitempty"`
}
// CodeVersion The desired code version.
type CodeVersion = int64
// Collector Collector model.
type Collector struct {
// ActiveVersion The desired version.
ActiveVersion Version `json:"active_version"`
// ClientId The client external id
ClientId ClientID `json:"client_id"`
// Fields The fields in the collector.
Fields CollectorFields `json:"fields"`
// LatestVersion The desired version.
LatestVersion Version `json:"latest_version"`
// MinimumCleanerVersion The desired code version.
MinimumCleanerVersion CodeVersion `json:"minimum_cleaner_version"`
// MinimumTextVersion The desired code version.
MinimumTextVersion CodeVersion `json:"minimum_text_version"`
}
// CollectorField The field properties for the collector.
type CollectorField struct {
// Name The output field name.
Name CollectorFieldName `json:"name"`
// QueryId The query id.
QueryId QueryID `json:"query_id"`
}
// CollectorFieldName The output field name.
type CollectorFieldName = string
// CollectorFields The fields in the collector.
type CollectorFields = []CollectorField
// CollectorSet Payload for updating a Collector.
type CollectorSet struct {
// ActiveVersion The desired version.
ActiveVersion *Version `json:"active_version,omitempty"`
// Fields The fields in the collector.
Fields *CollectorFields `json:"fields,omitempty"`
// MinimumCleanerVersion The desired code version.
MinimumCleanerVersion *CodeVersion `json:"minimum_cleaner_version,omitempty"`
// MinimumTextVersion The desired code version.
MinimumTextVersion *CodeVersion `json:"minimum_text_version,omitempty"`
}
// DocClient The properties of a client.
type DocClient struct {
// CanSync If the client is allowing active syncs
CanSync ClientCanSync `json:"can_sync"`
// Id The client external id
Id ClientID `json:"id"`
// Name The client name
Name ClientName `json:"name"`
// Uid The client internal unique id
Uid ClientUID `json:"uid"`
}
// Document The document properties.
type Document struct {
// ClientId The client external id
ClientId ClientID `json:"client_id"`
// Fields The fields and the value for the document
Fields map[string]interface{} `json:"fields"`
// Hash The document hash
Hash Hash `json:"hash"`
// Id The document id.
Id DocumentID `json:"id"`
}
// DocumentID The document id.
type DocumentID = openapi_types.UUID
// DocumentSummary The document properties.
type DocumentSummary struct {
// Hash The document hash
Hash Hash `json:"hash"`
// Id The document id.
Id DocumentID `json:"id"`
}
// ExportDetails Payload for export trigger response.
type ExportDetails struct {
// ClientId The client external id
ClientId ClientID `json:"client_id"`
// OutputLocation The location in which the export zip file will be found.
OutputLocation *string `json:"output_location,omitempty"`
// Status The possible export states.
Status ExportStatus `json:"status"`
}
// ExportID The export id.
type ExportID = openapi_types.UUID
// ExportStatus The possible export states.
type ExportStatus string
// ExportTrigger Payload for triggering an export.
type ExportTrigger struct {
// FieldFilters Filter the scope based on field output values.
FieldFilters *[]FieldFilter `json:"field_filters,omitempty"`
// IngestionFilters Filter the scope based on ingestion parameters.
IngestionFilters *struct {
// EndDate The last date of ingestion.
EndDate *time.Time `json:"end_date,omitempty"`
// StartDate This first date of ingestion.
StartDate *time.Time `json:"start_date,omitempty"`
} `json:"ingestion_filters,omitempty"`
}
// FieldFilter Filtering a column
type FieldFilter struct {
// Condition The possible field filtering conditions.
Condition FieldFilterCondition `json:"condition"`
// FieldName The output field name.
FieldName CollectorFieldName `json:"field_name"`
// Values The values useful to the filter.
Values []string `json:"values"`
}
// FieldFilterCondition The possible field filtering conditions.
type FieldFilterCondition string
// Hash The document hash
type Hash = string
// IdMessage A single uuid.
type IdMessage struct {
// Id Unique identifier for entity.
Id openapi_types.UUID `json:"id"`
}
// ListDocuments The documents in the client.
type ListDocuments = []DocumentSummary
// ListQueries A set of queries.
type ListQueries struct {
// Queries List of queries.
Queries []Query `json:"queries"`
}
// Query A logic unit of execution.
type Query struct {
// ActiveVersion The desired version.
ActiveVersion Version `json:"active_version"`
// Config Configuration for the query.
Config *QueryConfig `json:"config,omitempty"`
// Id The query id.
Id QueryID `json:"id"`
// LatestVersion The desired version.
LatestVersion Version `json:"latest_version"`
// RequiredQueries List of required query IDs.
RequiredQueries *RequiredQueryIDs `json:"required_queries,omitempty"`
// Type Specifies the type of the query.
Type QueryType `json:"type"`
}
// QueryConfig Configuration for the query.
type QueryConfig = string
// QueryCreate The parameters required to create a query.
type QueryCreate struct {
// Config Configuration for the query.
Config *QueryConfig `json:"config,omitempty"`
// RequiredQueries List of required query IDs.
RequiredQueries *RequiredQueryIDs `json:"required_queries,omitempty"`
// Type Specifies the type of the query.
Type QueryType `json:"type"`
}
// QueryID The query id.
type QueryID = openapi_types.UUID
// QueryTestRequest The properties for a query test request.
type QueryTestRequest struct {
// DocumentId The document id.
DocumentId DocumentID `json:"document_id"`
// QueryVersion The desired version.
QueryVersion Version `json:"query_version"`
}
// QueryTestResponse The response from a query test.
type QueryTestResponse struct {
// Value Result of the query test.
Value string `json:"value"`
}
// QueryType Specifies the type of the query.
type QueryType string
// QueryUpdate The properties that may be updated for a query.
type QueryUpdate struct {
// ActiveVersion The desired version.
ActiveVersion *Version `json:"active_version,omitempty"`
// Config Configuration for the query.
Config *QueryConfig `json:"config,omitempty"`
// RequiredQueries List of required query IDs.
RequiredQueries *RequiredQueryIDs `json:"required_queries,omitempty"`
}
// RequiredQueryIDs List of required query IDs.
type RequiredQueryIDs = []QueryID
// Version The desired version.
type Version = int32
// CreateClientJSONRequestBody defines body for CreateClient for application/json ContentType.
type CreateClientJSONRequestBody = ClientCreate
// UpdateClientJSONRequestBody defines body for UpdateClient for application/json ContentType.
type UpdateClientJSONRequestBody = ClientUpdate
// SetCollectorByClientIdJSONRequestBody defines body for SetCollectorByClientId for application/json ContentType.
type SetCollectorByClientIdJSONRequestBody = CollectorSet
// TriggerExportJSONRequestBody defines body for TriggerExport for application/json ContentType.
type TriggerExportJSONRequestBody = ExportTrigger
// CreateQueryJSONRequestBody defines body for CreateQuery for application/json ContentType.
type CreateQueryJSONRequestBody = QueryCreate
// UpdateQueryJSONRequestBody defines body for UpdateQuery for application/json ContentType.
type UpdateQueryJSONRequestBody = QueryUpdate
// TestQueryJSONRequestBody defines body for TestQuery for application/json ContentType.
type TestQueryJSONRequestBody = QueryTestRequest
// ServerInterface represents all server handlers.
type ServerInterface interface {
// Create a new client
// (POST /client)
CreateClient(ctx echo.Context) error
// Get a client by ID
// (GET /client/{id})
GetClient(ctx echo.Context, id ClientID) error
// Update a client
// (PATCH /client/{id})
UpdateClient(ctx echo.Context, id ClientID) error
// Get a collector by client ID
// (GET /client/{id}/collector)
GetCollectorByClientId(ctx echo.Context, id ClientID) error
// Set a collector
// (PATCH /client/{id}/collector)
SetCollectorByClientId(ctx echo.Context, id ClientID) error
// List the documents for a client
// (GET /client/{id}/documents)
ListDocumentsByClientId(ctx echo.Context, id ClientID) error
// Trigger an export
// (POST /client/{id}/export)
TriggerExport(ctx echo.Context, id ClientID) error
// Get client sync status
// (GET /client/{id}/status)
GetStatusByClientId(ctx echo.Context, id ClientID) error
// Get document details by its id
// (GET /document/{id})
GetDocument(ctx echo.Context, id DocumentID) error
// Check export state.
// (GET /export/{id})
ExportState(ctx echo.Context, id ExportID) error
// List queries
// (GET /query)
ListQueries(ctx echo.Context) error
// Create a new query
// (POST /query)
CreateQuery(ctx echo.Context) error
// Get a query by ID
// (GET /query/{id})
GetQuery(ctx echo.Context, id QueryID) error
// Update a query
// (PATCH /query/{id})
UpdateQuery(ctx echo.Context, id QueryID) error
// Test a query
// (POST /query/{id}/test)
TestQuery(ctx echo.Context, id QueryID) error
}
// ServerInterfaceWrapper converts echo contexts to parameters.
type ServerInterfaceWrapper struct {
Handler ServerInterface
}
// CreateClient converts echo context to params.
func (w *ServerInterfaceWrapper) CreateClient(ctx echo.Context) error {
var err error
ctx.Set(PlaceholderAuthScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.CreateClient(ctx)
return err
}
// GetClient converts echo context to params.
func (w *ServerInterfaceWrapper) GetClient(ctx echo.Context) error {
var err error
// ------------- Path parameter "id" -------------
var id ClientID
err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err))
}
ctx.Set(PlaceholderAuthScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.GetClient(ctx, id)
return err
}
// UpdateClient converts echo context to params.
func (w *ServerInterfaceWrapper) UpdateClient(ctx echo.Context) error {
var err error
// ------------- Path parameter "id" -------------
var id ClientID
err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err))
}
ctx.Set(PlaceholderAuthScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.UpdateClient(ctx, id)
return err
}
// GetCollectorByClientId converts echo context to params.
func (w *ServerInterfaceWrapper) GetCollectorByClientId(ctx echo.Context) error {
var err error
// ------------- Path parameter "id" -------------
var id ClientID
err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err))
}
ctx.Set(PlaceholderAuthScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.GetCollectorByClientId(ctx, id)
return err
}
// SetCollectorByClientId converts echo context to params.
func (w *ServerInterfaceWrapper) SetCollectorByClientId(ctx echo.Context) error {
var err error
// ------------- Path parameter "id" -------------
var id ClientID
err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err))
}
ctx.Set(PlaceholderAuthScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.SetCollectorByClientId(ctx, id)
return err
}
// ListDocumentsByClientId converts echo context to params.
func (w *ServerInterfaceWrapper) ListDocumentsByClientId(ctx echo.Context) error {
var err error
// ------------- Path parameter "id" -------------
var id ClientID
err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err))
}
ctx.Set(PlaceholderAuthScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.ListDocumentsByClientId(ctx, id)
return err
}
// TriggerExport converts echo context to params.
func (w *ServerInterfaceWrapper) TriggerExport(ctx echo.Context) error {
var err error
// ------------- Path parameter "id" -------------
var id ClientID
err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err))
}
ctx.Set(PlaceholderAuthScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.TriggerExport(ctx, id)
return err
}
// GetStatusByClientId converts echo context to params.
func (w *ServerInterfaceWrapper) GetStatusByClientId(ctx echo.Context) error {
var err error
// ------------- Path parameter "id" -------------
var id ClientID
err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err))
}
ctx.Set(PlaceholderAuthScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.GetStatusByClientId(ctx, id)
return err
}
// GetDocument converts echo context to params.
func (w *ServerInterfaceWrapper) GetDocument(ctx echo.Context) error {
var err error
// ------------- Path parameter "id" -------------
var id DocumentID
err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err))
}
ctx.Set(PlaceholderAuthScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.GetDocument(ctx, id)
return err
}
// ExportState converts echo context to params.
func (w *ServerInterfaceWrapper) ExportState(ctx echo.Context) error {
var err error
// ------------- Path parameter "id" -------------
var id ExportID
err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err))
}
ctx.Set(PlaceholderAuthScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.ExportState(ctx, id)
return err
}
// ListQueries converts echo context to params.
func (w *ServerInterfaceWrapper) ListQueries(ctx echo.Context) error {
var err error
ctx.Set(PlaceholderAuthScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.ListQueries(ctx)
return err
}
// CreateQuery converts echo context to params.
func (w *ServerInterfaceWrapper) CreateQuery(ctx echo.Context) error {
var err error
ctx.Set(PlaceholderAuthScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.CreateQuery(ctx)
return err
}
// GetQuery converts echo context to params.
func (w *ServerInterfaceWrapper) GetQuery(ctx echo.Context) error {
var err error
// ------------- Path parameter "id" -------------
var id QueryID
err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err))
}
ctx.Set(PlaceholderAuthScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.GetQuery(ctx, id)
return err
}
// UpdateQuery converts echo context to params.
func (w *ServerInterfaceWrapper) UpdateQuery(ctx echo.Context) error {
var err error
// ------------- Path parameter "id" -------------
var id QueryID
err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err))
}
ctx.Set(PlaceholderAuthScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.UpdateQuery(ctx, id)
return err
}
// TestQuery converts echo context to params.
func (w *ServerInterfaceWrapper) TestQuery(ctx echo.Context) error {
var err error
// ------------- Path parameter "id" -------------
var id QueryID
err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err))
}
ctx.Set(PlaceholderAuthScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.TestQuery(ctx, id)
return err
}
// This is a simple interface which specifies echo.Route addition functions which
// are present on both echo.Echo and echo.Group, since we want to allow using
// either of them for path registration
type EchoRouter interface {
CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
}
// RegisterHandlers adds each server route to the EchoRouter.
func RegisterHandlers(router EchoRouter, si ServerInterface) {
RegisterHandlersWithBaseURL(router, si, "")
}
// Registers handlers, and prepends BaseURL to the paths, so that the paths
// can be served under a prefix.
func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) {
wrapper := ServerInterfaceWrapper{
Handler: si,
}
router.POST(baseURL+"/client", wrapper.CreateClient)
router.GET(baseURL+"/client/:id", wrapper.GetClient)
router.PATCH(baseURL+"/client/:id", wrapper.UpdateClient)
router.GET(baseURL+"/client/:id/collector", wrapper.GetCollectorByClientId)
router.PATCH(baseURL+"/client/:id/collector", wrapper.SetCollectorByClientId)
router.GET(baseURL+"/client/:id/documents", wrapper.ListDocumentsByClientId)
router.POST(baseURL+"/client/:id/export", wrapper.TriggerExport)
router.GET(baseURL+"/client/:id/status", wrapper.GetStatusByClientId)
router.GET(baseURL+"/document/:id", wrapper.GetDocument)
router.GET(baseURL+"/export/:id", wrapper.ExportState)
router.GET(baseURL+"/query", wrapper.ListQueries)
router.POST(baseURL+"/query", wrapper.CreateQuery)
router.GET(baseURL+"/query/:id", wrapper.GetQuery)
router.PATCH(baseURL+"/query/:id", wrapper.UpdateQuery)
router.POST(baseURL+"/query/:id/test", wrapper.TestQuery)
}
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
"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",
"7wAAAP//OVAUyb5OAAA=",
}
// GetSwagger returns the content of the embedded swagger specification file
// or error if failed to decode
func decodeSpec() ([]byte, error) {
zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, ""))
if err != nil {
return nil, fmt.Errorf("error base64 decoding spec: %w", err)
}
zr, err := gzip.NewReader(bytes.NewReader(zipped))
if err != nil {
return nil, fmt.Errorf("error decompressing spec: %w", err)
}
var buf bytes.Buffer
_, err = buf.ReadFrom(zr)
if err != nil {
return nil, fmt.Errorf("error decompressing spec: %w", err)
}
return buf.Bytes(), nil
}
var rawSpec = decodeSpecCached()
// a naive cached of a decoded swagger spec
func decodeSpecCached() func() ([]byte, error) {
data, err := decodeSpec()
return func() ([]byte, error) {
return data, err
}
}
// Constructs a synthetic filesystem for resolving external references when loading openapi specifications.
func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) {
res := make(map[string]func() ([]byte, error))
if len(pathToFile) > 0 {
res[pathToFile] = rawSpec
}
return res
}
// GetSwagger returns the Swagger specification corresponding to the generated code
// in this file. The external references of Swagger specification are resolved.
// The logic of resolving external references is tightly connected to "import-mapping" feature.
// Externally referenced files must be embedded in the corresponding golang packages.
// Urls can be supported but this task was out of the scope.
func GetSwagger() (swagger *openapi3.T, err error) {
resolvePath := PathToRawSpec("")
loader := openapi3.NewLoader()
loader.IsExternalRefsAllowed = true
loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) {
pathToFile := url.String()
pathToFile = path.Clean(pathToFile)
getSpec, ok := resolvePath[pathToFile]
if !ok {
err1 := fmt.Errorf("path not found: %s", pathToFile)
return nil, err1
}
return getSpec()
}
var specData []byte
specData, err = rawSpec()
if err != nil {
return
}
swagger, err = loader.LoadFromData(specData)
if err != nil {
return
}
return
}
+60
View File
@@ -0,0 +1,60 @@
package queryapi
import (
"fmt"
"net/http"
"queryorchestration/internal/client"
"github.com/labstack/echo/v4"
)
func (s *Controllers) CreateClient(ctx echo.Context) error {
req := ClientCreate{}
if err := ctx.Bind(&req); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err)
}
_, err := s.svc.Client.Create(ctx.Request().Context(), client.CreateParams{
Name: req.Name,
ExternalId: req.Id,
})
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unable to create client: %s", err))
}
return ctx.JSON(http.StatusCreated, ClientIDBody{
Id: req.Id,
})
}
func (s *Controllers) GetClient(ctx echo.Context, id ClientID) error {
client, err := s.svc.Client.GetByExternalId(ctx.Request().Context(), id)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unable to get client: %s", err))
}
return ctx.JSON(http.StatusOK, DocClient{
Id: client.ExternalID,
Uid: client.ID,
Name: client.Name,
CanSync: client.CanSync,
})
}
func (s *Controllers) UpdateClient(ctx echo.Context, id ClientID) error {
req := ClientUpdate{}
if err := ctx.Bind(&req); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err)
}
err := s.svc.Client.UpdateByExternalId(ctx.Request().Context(), id, &client.Update{
Name: req.Name,
CanSync: req.CanSync,
})
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unable to update query: %s", err))
}
return ctx.NoContent(http.StatusOK)
}
+151
View File
@@ -0,0 +1,151 @@
package queryapi_test
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
queryapi "queryorchestration/api/queryAPI"
"queryorchestration/internal/client"
"queryorchestration/internal/database"
"queryorchestration/internal/database/repository"
"queryorchestration/internal/serviceconfig"
"github.com/go-playground/validator/v10"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
"github.com/pashagolub/pgxmock/v3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCreateClient(t *testing.T) {
pool, err := pgxmock.NewPool()
require.NoError(t, err)
cfg := &serviceconfig.BaseConfig{}
cfg.DBPool = pool
cfg.DBQueries = repository.New(pool)
cons := queryapi.NewControllers(validator.New(), &queryapi.Services{
Client: client.New(cfg),
})
body := queryapi.ClientCreate{
Name: "example_name",
Id: "external_id",
}
bodyBytes, err := json.Marshal(body)
assert.NoError(t, err)
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(bodyBytes)))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
ctx := e.NewContext(req, rec)
pool.ExpectQuery("name: CreateClient :one").WithArgs(body.Id, body.Name).WillReturnRows(
pgxmock.NewRows([]string{"id"}).
AddRow(database.MustToDBUUID(uuid.New())),
)
err = cons.CreateClient(ctx)
assert.NoError(t, err)
assert.Equal(t, http.StatusCreated, rec.Code)
assert.Equal(t, fmt.Sprintf("{\"id\":\"%s\"}\n", body.Id), rec.Body.String())
}
func TestGetClient(t *testing.T) {
pool, err := pgxmock.NewPool()
require.NoError(t, err)
cfg := &serviceconfig.BaseConfig{}
cfg.DBPool = pool
cfg.DBQueries = repository.New(pool)
cons := queryapi.NewControllers(validator.New(), &queryapi.Services{
Client: client.New(cfg),
})
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(""))
rec := httptest.NewRecorder()
ctx := e.NewContext(req, rec)
id := "client_id"
clientUid := uuid.New()
ctx.Set("id", id)
pool.ExpectQuery("name: GetClientByExternalId :one").WithArgs(id).WillReturnRows(
pgxmock.NewRows([]string{"id", "externalId", "name", "canSync"}).
AddRow(database.MustToDBUUID(clientUid), "client_id", "client_name", true),
)
err = cons.GetClient(ctx, id)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
var res queryapi.DocClient
err = json.Unmarshal(rec.Body.Bytes(), &res)
assert.NoError(t, err)
assert.EqualExportedValues(t, queryapi.DocClient{
Id: id,
Uid: clientUid,
Name: "client_name",
CanSync: true,
}, res)
}
func TestUpdateClient(t *testing.T) {
pool, err := pgxmock.NewPool()
require.NoError(t, err)
cfg := &serviceconfig.BaseConfig{}
cfg.DBPool = pool
cfg.DBQueries = repository.New(pool)
cons := queryapi.NewControllers(validator.New(), &queryapi.Services{
Client: client.New(cfg),
})
cs := false
body := queryapi.ClientUpdate{
CanSync: &cs,
}
bodyBytes, err := json.Marshal(body)
assert.NoError(t, err)
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(bodyBytes)))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
ctx := e.NewContext(req, rec)
id := "clientid"
ctx.Set("id", id)
intid := uuid.New()
pool.ExpectQuery("name: GetClientByExternalId :one").WithArgs(id).WillReturnRows(
pgxmock.NewRows([]string{"id", "externalId", "name", "canSync"}).
AddRow(database.MustToDBUUID(intid), "clientid", "client_name", true),
)
pool.ExpectQuery("name: GetClient :one").WithArgs(database.MustToDBUUID(intid)).WillReturnRows(
pgxmock.NewRows([]string{"id", "externalId", "name", "canSync"}).
AddRow(database.MustToDBUUID(intid), "clientid", "client_name", true),
)
pool.ExpectBegin()
pool.ExpectExec("name: AddClientCanSync :exec").WithArgs(*body.CanSync, database.MustToDBUUID(intid)).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectCommit()
err = cons.UpdateClient(ctx, id)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
assert.Empty(t, rec.Body.String())
}
+65
View File
@@ -0,0 +1,65 @@
package queryapi
import (
"fmt"
"net/http"
collectorset "queryorchestration/internal/collector/set"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
)
func (s *Controllers) GetCollectorByClientId(ctx echo.Context, clientId ClientID) error {
coll, err := s.svc.Collector.GetByClientExternalID(ctx.Request().Context(), clientId)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unable to get collector: %s", err))
}
fields := make([]CollectorField, len(coll.Fields))
index := 0
for name, id := range coll.Fields {
fields[index] = CollectorField{
Name: name,
QueryId: id,
}
index++
}
return ctx.JSON(http.StatusOK, Collector{
ClientId: clientId,
MinimumCleanerVersion: coll.MinCleanVersion,
MinimumTextVersion: coll.MinTextVersion,
ActiveVersion: coll.ActiveVersion,
LatestVersion: coll.LatestVersion,
Fields: fields,
})
}
func (s *Controllers) SetCollectorByClientId(ctx echo.Context, clientId ClientID) error {
req := CollectorSet{}
if err := ctx.Bind(&req); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err)
}
var fields *map[string]uuid.UUID
if req.Fields != nil {
fs := map[string]uuid.UUID{}
for _, field := range *req.Fields {
fs[field.Name] = field.QueryId
}
fields = &fs
}
err := s.svc.CollectorSet.SetByClientExternalId(ctx.Request().Context(), clientId, &collectorset.SetParams{
ActiveVersion: req.ActiveVersion,
MinCleanVersion: req.MinimumCleanerVersion,
MinTextVersion: req.MinimumTextVersion,
Fields: fields,
})
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unable to update query: %s", err))
}
return ctx.NoContent(http.StatusOK)
}
+170
View File
@@ -0,0 +1,170 @@
package queryapi_test
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
queryapi "queryorchestration/api/queryAPI"
"queryorchestration/internal/collector"
collectorset "queryorchestration/internal/collector/set"
"queryorchestration/internal/database"
"queryorchestration/internal/database/repository"
"queryorchestration/internal/serviceconfig"
"queryorchestration/internal/serviceconfig/queue/clientsync"
queuemock "queryorchestration/mocks/queue"
"github.com/aws/aws-sdk-go-v2/service/sqs"
"github.com/go-playground/validator/v10"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype"
"github.com/labstack/echo/v4"
"github.com/pashagolub/pgxmock/v3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
func TestSetCollector(t *testing.T) {
pool, err := pgxmock.NewPool()
require.NoError(t, err)
cfg := &struct {
serviceconfig.BaseConfig
clientsync.ClientSyncConfig
}{}
cfg.DBPool = pool
cfg.DBQueries = repository.New(pool)
mockSQS := queuemock.NewMockSQSClient(t)
cfg.QueueClient = mockSQS
current := collector.Collector{
ClientID: uuid.New(),
ActiveVersion: 1,
LatestVersion: 4,
}
av := int32(2)
cv := int64(1)
body := queryapi.CollectorSet{
ActiveVersion: &av,
MinimumCleanerVersion: &cv,
Fields: &[]queryapi.CollectorField{
{
Name: "a",
QueryId: uuid.New(),
},
},
}
bodyBytes, err := json.Marshal(body)
assert.NoError(t, err)
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(bodyBytes)))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
ctx := e.NewContext(req, rec)
cons := queryapi.NewControllers(validator.New(), &queryapi.Services{
CollectorSet: collectorset.New(cfg, &collectorset.Services{
Collector: collector.New(cfg),
}),
})
id := "clientid"
ctx.Set("id", id)
pool.ExpectQuery("name: GetClientByExternalId :one").WithArgs(id).WillReturnRows(
pgxmock.NewRows([]string{"id", "externalId", "name", "can_sync"}).
AddRow(database.MustToDBUUID(current.ClientID), id, "name", true),
)
pool.ExpectQuery("name: GetCollectorByClientID :one").WithArgs(database.MustToDBUUID(current.ClientID)).
WillReturnRows(
pgxmock.NewRows([]string{"clientId", "minCleanVersion", "minTextVersion", "activeVersion", "latestVersion", "fields"}).
AddRow(database.MustToDBUUID(current.ClientID), current.MinCleanVersion, current.MinTextVersion, current.ActiveVersion, current.LatestVersion, []byte("")),
)
pool.ExpectQuery("name: AllQueriesExist :one").WithArgs([]pgtype.UUID{database.MustToDBUUID((*body.Fields)[0].QueryId)}).
WillReturnRows(
pgxmock.NewRows([]string{"exist"}).
AddRow(true),
)
pool.ExpectBeginTx(pgx.TxOptions{})
pool.ExpectQuery("name: AddLatestCollectorVersion :one").WithArgs(database.MustToDBUUID(current.ClientID)).WillReturnRows(
pgxmock.NewRows([]string{"version"}).
AddRow(int32(5)),
)
pool.ExpectExec("name: SetActiveCollectorVersion :exec").WithArgs(database.MustToDBUUID(current.ClientID), int32(2)).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectExec("name: SetCollectorCleanVersion :exec").WithArgs(database.MustToDBUUID(current.ClientID), int32(5), *body.MinimumCleanerVersion).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectExec("name: AddCollectorQuery :exec").WithArgs(database.MustToDBUUID(current.ClientID), "a", database.MustToDBUUID((*body.Fields)[0].QueryId), int32(5)).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectCommit()
mockSQS.EXPECT().
SendMessage(
mock.Anything,
mock.MatchedBy(func(in *sqs.SendMessageInput) bool {
return *in.QueueUrl == cfg.ClientSyncURL && *in.MessageBody == fmt.Sprintf("{\"id\":\"%s\"}", current.ClientID.String())
}),
mock.Anything,
).
Return(&sqs.SendMessageOutput{}, nil)
err = cons.SetCollectorByClientId(ctx, id)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
assert.Empty(t, rec.Body.String())
}
func TestGetCollectorByclientId(t *testing.T) {
pool, err := pgxmock.NewPool()
require.NoError(t, err)
cfg := &serviceconfig.BaseConfig{}
cfg.DBPool = pool
cfg.DBQueries = repository.New(pool)
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(""))
rec := httptest.NewRecorder()
ctx := e.NewContext(req, rec)
svc := collector.New(cfg)
cons := queryapi.NewControllers(validator.New(), &queryapi.Services{
Collector: svc,
})
coll := collector.Collector{
ClientID: uuid.New(),
MinCleanVersion: 1,
MinTextVersion: 2,
}
id := "clientid"
pool.ExpectQuery("name: GetCollectorByClientExternalID :one").WithArgs(id).
WillReturnRows(
pgxmock.NewRows([]string{"clientId", "minCleanVersion", "minTextVersion", "activeVersion", "latestVersion", "fields"}).
AddRow(database.MustToDBUUID(coll.ClientID), coll.MinCleanVersion, coll.MinTextVersion, int32(1), int32(2), []byte("")),
)
err = cons.GetCollectorByClientId(ctx, id)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
var res queryapi.Collector
err = json.Unmarshal(rec.Body.Bytes(), &res)
assert.NoError(t, err)
assert.EqualExportedValues(t, queryapi.Collector{
ClientId: id,
MinimumCleanerVersion: coll.MinCleanVersion,
MinimumTextVersion: coll.MinTextVersion,
ActiveVersion: int32(1),
LatestVersion: int32(2),
Fields: []queryapi.CollectorField{},
}, res)
}
+39
View File
@@ -0,0 +1,39 @@
package queryapi
import (
"queryorchestration/internal/client"
"queryorchestration/internal/collector"
collectorset "queryorchestration/internal/collector/set"
"queryorchestration/internal/document"
"queryorchestration/internal/export"
"queryorchestration/internal/query"
querytest "queryorchestration/internal/query/test"
queryupdate "queryorchestration/internal/query/update"
"github.com/go-playground/validator/v10"
)
const Name = "queryAPI"
type Services struct {
Export *export.Service
Collector *collector.Service
CollectorSet *collectorset.Service
Query *query.Service
QueryUpdate *queryupdate.Service
QueryTest *querytest.Service
Client *client.Service
Document *document.Service
}
type Controllers struct {
validator *validator.Validate
svc *Services
}
func NewControllers(validator *validator.Validate, services *Services) *Controllers {
return &Controllers{
validator: validator,
svc: services,
}
}
+15
View File
@@ -0,0 +1,15 @@
package queryapi_test
import (
"testing"
queryapi "queryorchestration/api/queryAPI"
"github.com/go-playground/validator/v10"
"github.com/stretchr/testify/assert"
)
func TestNewControllers(t *testing.T) {
cons := queryapi.NewControllers(validator.New(), &queryapi.Services{})
assert.NotNil(t, cons)
}
+39
View File
@@ -0,0 +1,39 @@
package queryapi
import (
"fmt"
"net/http"
"github.com/labstack/echo/v4"
)
func (s *Controllers) ListDocumentsByClientId(ctx echo.Context, clientId ClientID) error {
documents, err := s.svc.Document.ListByClientExternalId(ctx.Request().Context(), clientId)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unable to list documents: %s", err))
}
docs := make([]DocumentSummary, len(documents))
for i, doc := range documents {
docs[i] = DocumentSummary{
Id: doc.ID,
Hash: doc.Hash,
}
}
return ctx.JSON(http.StatusOK, docs)
}
func (s *Controllers) GetDocument(ctx echo.Context, id DocumentID) error {
document, err := s.svc.Document.GetExternal(ctx.Request().Context(), id)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unable to list documents: %s", err))
}
return ctx.JSON(http.StatusOK, Document{
Id: document.Id,
ClientId: document.ClientID,
Hash: document.Hash,
Fields: document.Fields,
})
}
+122
View File
@@ -0,0 +1,122 @@
package queryapi_test
import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
queryapi "queryorchestration/api/queryAPI"
"queryorchestration/internal/database"
"queryorchestration/internal/database/repository"
"queryorchestration/internal/document"
"queryorchestration/internal/serviceconfig"
"queryorchestration/internal/serviceconfig/queue/clientsync"
queuemock "queryorchestration/mocks/queue"
"github.com/go-playground/validator/v10"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
"github.com/pashagolub/pgxmock/v3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestListDocumentsByClientId(t *testing.T) {
pool, err := pgxmock.NewPool()
require.NoError(t, err)
cfg := &struct {
serviceconfig.BaseConfig
clientsync.ClientSyncConfig
}{}
cfg.DBPool = pool
cfg.DBQueries = repository.New(pool)
mockSQS := queuemock.NewMockSQSClient(t)
cfg.QueueClient = mockSQS
clientId := "clientid"
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(""))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
ctx := e.NewContext(req, rec)
cons := queryapi.NewControllers(validator.New(), &queryapi.Services{
Document: document.New(cfg),
})
ctx.Set("id", clientId)
doc := queryapi.ListDocuments{
{
Id: uuid.New(),
Hash: "hash",
},
}
pool.ExpectQuery("name: ListDocumentsByClientExternalId :many").WithArgs(clientId).
WillReturnRows(
pgxmock.NewRows([]string{"id", "hash"}).
AddRow(database.MustToDBUUID(doc[0].Id), "hash"),
)
err = cons.ListDocumentsByClientId(ctx, clientId)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
var res queryapi.ListDocuments
err = json.Unmarshal(rec.Body.Bytes(), &res)
assert.NoError(t, err)
assert.EqualExportedValues(t, doc, res)
}
func TestGetDocument(t *testing.T) {
pool, err := pgxmock.NewPool()
require.NoError(t, err)
cfg := &struct {
serviceconfig.BaseConfig
clientsync.ClientSyncConfig
}{}
cfg.DBPool = pool
cfg.DBQueries = repository.New(pool)
mockSQS := queuemock.NewMockSQSClient(t)
cfg.QueueClient = mockSQS
docId := uuid.New()
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(""))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
ctx := e.NewContext(req, rec)
cons := queryapi.NewControllers(validator.New(), &queryapi.Services{
Document: document.New(cfg),
})
ctx.Set("id", docId)
doc := queryapi.Document{
Id: docId,
ClientId: "externalID",
Hash: "example",
Fields: map[string]interface{}{
"json": "hello",
},
}
pool.ExpectQuery("name: GetDocumentExternal :one").WithArgs(database.MustToDBUUID(docId)).
WillReturnRows(
pgxmock.NewRows([]string{"id", "clientId", "hash", "fields"}).
AddRow(database.MustToDBUUID(doc.Id), "externalID", "example", []byte(`{"json": "hello"}`)),
)
err = cons.GetDocument(ctx, docId)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
var res queryapi.Document
err = json.Unmarshal(rec.Body.Bytes(), &res)
assert.NoError(t, err)
assert.EqualExportedValues(t, doc, res)
}
+15
View File
@@ -0,0 +1,15 @@
package queryapi
import (
"net/http"
"github.com/labstack/echo/v4"
)
func (s *Controllers) TriggerExport(ctx echo.Context, clientId ClientID) error {
return ctx.JSON(http.StatusOK, map[string]string{"status": "export triggered"})
}
func (s *Controllers) ExportState(ctx echo.Context, id ExportID) error {
return ctx.JSON(http.StatusOK, map[string]string{"status": "export triggered"})
}
+45
View File
@@ -0,0 +1,45 @@
package queryapi_test
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
queryapi "queryorchestration/api/queryAPI"
"github.com/go-playground/validator/v10"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
)
func TestTriggerExport(t *testing.T) {
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(""))
rec := httptest.NewRecorder()
ctx := e.NewContext(req, rec)
cons := queryapi.NewControllers(validator.New(), &queryapi.Services{})
err := cons.TriggerExport(ctx, "clientid")
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
assert.NotEmpty(t, rec.Body.String())
}
func TestExportState(t *testing.T) {
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(""))
rec := httptest.NewRecorder()
ctx := e.NewContext(req, rec)
cons := queryapi.NewControllers(validator.New(), &queryapi.Services{})
id := uuid.New()
err := cons.ExportState(ctx, id)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
assert.NotEmpty(t, rec.Body.String())
}
+99
View File
@@ -0,0 +1,99 @@
package queryapi
import (
"errors"
"queryorchestration/internal/client"
"queryorchestration/internal/query"
resultprocessor "queryorchestration/internal/query/result/processor"
"github.com/google/uuid"
)
func parseQueries(queries []*query.Query) ([]Query, error) {
outQueries := make([]Query, len(queries))
for index, query := range queries {
qt, err := parseQuery(query)
if err != nil {
return nil, err
}
outQueries[index] = *qt
}
return outQueries, nil
}
func parseQuery(query *query.Query) (*Query, error) {
if query.RequiredQueryIDs != nil && len(*query.RequiredQueryIDs) == 0 {
query.RequiredQueryIDs = nil
}
qt, err := parseQueryType(query.Type)
if err != nil {
return nil, err
}
q := &Query{
Id: query.ID,
Type: qt,
ActiveVersion: query.ActiveVersion,
LatestVersion: query.LatestVersion,
RequiredQueries: query.RequiredQueryIDs,
Config: query.Config,
}
return q, nil
}
func parseQueryType(qType resultprocessor.Type) (QueryType, error) {
switch qType {
case resultprocessor.TypeJsonExtractor:
return JSONEXTRACTOR, nil
case resultprocessor.TypeContextFull:
return CONTEXTFULL, nil
default:
return "", errors.New("invalid query type")
}
}
func parseSpecQueryType(qType QueryType) (resultprocessor.Type, error) {
switch qType {
case JSONEXTRACTOR:
return resultprocessor.TypeJsonExtractor, nil
case CONTEXTFULL:
return resultprocessor.TypeContextFull, nil
default:
return resultprocessor.Type(-1), errors.New("invalid query type")
}
}
func parseStringToUUIDArray(sids *[]string) (*[]uuid.UUID, error) {
var ids []uuid.UUID
if sids != nil {
ids = make([]uuid.UUID, len(*sids))
for index, id := range *sids {
parsedID, err := uuid.Parse(id)
if err != nil {
return nil, errors.New("invalid required id")
}
ids[index] = parsedID
}
return &ids, nil
}
return nil, nil
}
func parseClientStatus(status client.Status) ClientStatus {
switch status {
case client.IN_SYNC:
return INSYNC
case client.NOT_SYNCING:
return NOTSYNCING
}
return NOTSYNCED
}
+142
View File
@@ -0,0 +1,142 @@
package queryapi
import (
"testing"
"queryorchestration/internal/client"
"queryorchestration/internal/query"
resultprocessor "queryorchestration/internal/query/result/processor"
"github.com/google/uuid"
"github.com/oapi-codegen/runtime/types"
"github.com/stretchr/testify/assert"
)
func TestParseQueries(t *testing.T) {
cfg := "hey"
in := []*query.Query{
{
ID: uuid.New(),
Type: resultprocessor.TypeContextFull,
ActiveVersion: 1,
LatestVersion: 2,
RequiredQueryIDs: &[]uuid.UUID{
uuid.New(),
},
Config: &cfg,
},
}
out, err := parseQueries(in)
assert.NoError(t, err)
assert.Len(t, out, len(in))
assert.ElementsMatch(t, []Query{
{
Id: in[0].ID,
Type: CONTEXTFULL,
ActiveVersion: 1,
LatestVersion: 2,
RequiredQueries: &[]types.UUID{
(*in[0].RequiredQueryIDs)[0],
},
Config: in[0].Config,
},
}, out)
}
func TestParseQuery(t *testing.T) {
cfg := "hey"
in := &query.Query{
ID: uuid.New(),
Type: resultprocessor.TypeContextFull,
ActiveVersion: 1,
LatestVersion: 2,
RequiredQueryIDs: &[]uuid.UUID{
uuid.New(),
},
Config: &cfg,
}
out, err := parseQuery(in)
assert.NoError(t, err)
assert.EqualExportedValues(t, Query{
Id: in.ID,
Type: CONTEXTFULL,
ActiveVersion: 1,
LatestVersion: 2,
RequiredQueries: &[]types.UUID{
(*in.RequiredQueryIDs)[0],
},
Config: in.Config,
},
*out)
}
func TestParseQueryMinimal(t *testing.T) {
in := &query.Query{
ID: uuid.New(),
Type: resultprocessor.TypeContextFull,
ActiveVersion: 1,
LatestVersion: 2,
}
out, err := parseQuery(in)
assert.NoError(t, err)
assert.EqualExportedValues(t, Query{
Id: in.ID,
Type: CONTEXTFULL,
ActiveVersion: 1,
LatestVersion: 2,
},
*out)
}
func TestParseQueryType(t *testing.T) {
qt, err := parseQueryType(resultprocessor.TypeContextFull)
assert.NoError(t, err)
assert.Equal(t, CONTEXTFULL, qt)
qt, err = parseQueryType(resultprocessor.TypeJsonExtractor)
assert.NoError(t, err)
assert.Equal(t, JSONEXTRACTOR, qt)
_, err = parseQueryType(resultprocessor.Type(-1))
assert.Error(t, err)
}
func TestParseSpecQueryType(t *testing.T) {
qt, err := parseSpecQueryType(CONTEXTFULL)
assert.NoError(t, err)
assert.Equal(t, resultprocessor.Type(resultprocessor.TypeContextFull), qt)
qt, err = parseSpecQueryType(JSONEXTRACTOR)
assert.NoError(t, err)
assert.Equal(t, resultprocessor.Type(resultprocessor.TypeJsonExtractor), qt)
_, err = parseSpecQueryType("invalid")
assert.Error(t, err)
}
func TestParseStringToUUIDArray(t *testing.T) {
ids := []uuid.UUID{uuid.New()}
out, err := parseStringToUUIDArray(&[]string{ids[0].String()})
assert.NoError(t, err)
assert.ElementsMatch(t, ids, *out)
_, err = parseStringToUUIDArray(&[]string{"invalid_uuid"})
assert.Error(t, err)
out, err = parseStringToUUIDArray(nil)
assert.NoError(t, err)
assert.Nil(t, out)
}
func TestParseSpecClientStatus(t *testing.T) {
qt := parseClientStatus(client.IN_SYNC)
assert.Equal(t, INSYNC, qt)
qt = parseClientStatus(client.NOT_SYNCED)
assert.Equal(t, NOTSYNCED, qt)
qt = parseClientStatus(client.NOT_SYNCING)
assert.Equal(t, NOTSYNCING, qt)
qt = parseClientStatus("invalid")
assert.Equal(t, NOTSYNCED, qt)
}
+105
View File
@@ -0,0 +1,105 @@
package queryapi
import (
"fmt"
"net/http"
"queryorchestration/internal/query/result"
resultprocessor "queryorchestration/internal/query/result/processor"
"github.com/labstack/echo/v4"
)
func (s *Controllers) ListQueries(ctx echo.Context) error {
queries, err := s.svc.Query.List(ctx.Request().Context())
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unable to list query: %s", err))
}
outQueries, err := parseQueries(queries)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unable to list query: %s", err))
}
return ctx.JSON(http.StatusOK, ListQueries{
Queries: outQueries,
})
}
func (s *Controllers) GetQuery(ctx echo.Context, id QueryID) error {
query, err := s.svc.Query.Get(ctx.Request().Context(), id)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unable to get query: %s", err))
}
qt, err := parseQuery(query)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unable to get query: %s", err))
}
return ctx.JSON(http.StatusOK, qt)
}
func (s *Controllers) CreateQuery(ctx echo.Context) error {
req := QueryCreate{}
if err := ctx.Bind(&req); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err)
}
qt, err := parseSpecQueryType(req.Type)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Invalid Query Type")
}
id, err := s.svc.Query.Create(ctx.Request().Context(), &resultprocessor.Create{
Type: qt,
RequiredQueryIDs: req.RequiredQueries,
Config: req.Config,
})
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unable to create query: %s", err))
}
return ctx.JSON(http.StatusCreated, IdMessage{
Id: id,
})
}
func (s *Controllers) UpdateQuery(ctx echo.Context, id QueryID) error {
req := QueryUpdate{}
if err := ctx.Bind(&req); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err)
}
err := s.svc.QueryUpdate.Update(ctx.Request().Context(), &resultprocessor.Update{
ActiveVersion: req.ActiveVersion,
Config: req.Config,
RequiredQueryIDs: req.RequiredQueries,
ID: id,
})
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unable to update query: %s", err))
}
return ctx.NoContent(http.StatusOK)
}
func (s *Controllers) TestQuery(ctx echo.Context, id QueryID) error {
req := QueryTestRequest{}
if err := ctx.Bind(&req); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err)
}
value, err := s.svc.QueryTest.Test(ctx.Request().Context(), result.Process{
QueryID: id,
QueryVersion: req.QueryVersion,
DocumentID: req.DocumentId,
})
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unable to test query: %s", err))
}
return ctx.JSON(http.StatusOK, QueryTestResponse{
Value: value,
})
}
+292
View File
@@ -0,0 +1,292 @@
package queryapi_test
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
queryapi "queryorchestration/api/queryAPI"
"queryorchestration/internal/collector"
"queryorchestration/internal/database"
"queryorchestration/internal/database/repository"
"queryorchestration/internal/document"
"queryorchestration/internal/query"
"queryorchestration/internal/query/result"
querytest "queryorchestration/internal/query/test"
queryupdate "queryorchestration/internal/query/update"
"queryorchestration/internal/serviceconfig"
"queryorchestration/internal/serviceconfig/queue/queryversionsync"
queuemock "queryorchestration/mocks/queue"
"github.com/stretchr/testify/require"
"github.com/aws/aws-sdk-go-v2/service/sqs"
"github.com/go-playground/validator/v10"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype"
"github.com/labstack/echo/v4"
"github.com/pashagolub/pgxmock/v3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
func TestCreateQuery(t *testing.T) {
pool, err := pgxmock.NewPool()
require.NoError(t, err)
cfg := &serviceconfig.BaseConfig{}
cfg.DBPool = pool
cfg.DBQueries = repository.New(pool)
cons := queryapi.NewControllers(validator.New(), &queryapi.Services{
Query: query.New(cfg),
})
body := queryapi.QueryCreate{
Type: queryapi.CONTEXTFULL,
}
bodyBytes, err := json.Marshal(body)
assert.NoError(t, err)
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(bodyBytes)))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
ctx := e.NewContext(req, rec)
id := uuid.New()
pool.ExpectBeginTx(pgx.TxOptions{})
pool.ExpectQuery("name: CreateQuery :one").WithArgs(repository.QuerytypeContextFull).WillReturnRows(
pgxmock.NewRows([]string{"id"}).
AddRow(database.MustToDBUUID(id)),
)
pool.ExpectQuery("name: AddLatestQueryVersion :one").WithArgs(database.MustToDBUUID(id)).WillReturnRows(
pgxmock.NewRows([]string{"version"}).
AddRow(int32(1)),
)
pool.ExpectExec("name: AddActiveQueryVersion :exec").WithArgs(database.MustToDBUUID(id), int32(1)).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectCommit()
err = cons.CreateQuery(ctx)
assert.NoError(t, err)
assert.Equal(t, http.StatusCreated, rec.Code)
assert.Equal(t, fmt.Sprintf("{\"id\":\"%s\"}\n", id), rec.Body.String())
}
func TestListQueries(t *testing.T) {
pool, err := pgxmock.NewPool()
require.NoError(t, err)
cfg := &serviceconfig.BaseConfig{}
cfg.DBPool = pool
cfg.DBQueries = repository.New(pool)
cons := queryapi.NewControllers(validator.New(), &queryapi.Services{
Query: query.New(cfg),
})
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(""))
rec := httptest.NewRecorder()
ctx := e.NewContext(req, rec)
id := uuid.New()
pool.ExpectQuery("name: ListQueries :many").WithArgs().WillReturnRows(
pgxmock.NewRows([]string{"id", "type", "activeVersion", "latestVersion", "config", "requiredIds"}).
AddRow(database.MustToDBUUID(id), repository.QuerytypeContextFull, int32(1), int32(2), []byte(""), []pgtype.UUID{}),
)
err = cons.ListQueries(ctx)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
var res queryapi.ListQueries
err = json.Unmarshal(rec.Body.Bytes(), &res)
assert.NoError(t, err)
assert.NotNil(t, res.Queries)
assert.ElementsMatch(t, res.Queries, []queryapi.Query{
{
Id: id,
Type: queryapi.CONTEXTFULL,
ActiveVersion: 1,
LatestVersion: 2,
},
})
}
func TestGetQuery(t *testing.T) {
pool, err := pgxmock.NewPool()
require.NoError(t, err)
cfg := &serviceconfig.BaseConfig{}
cfg.DBPool = pool
cfg.DBQueries = repository.New(pool)
cons := queryapi.NewControllers(validator.New(), &queryapi.Services{
Query: query.New(cfg),
})
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(""))
rec := httptest.NewRecorder()
ctx := e.NewContext(req, rec)
id := uuid.New()
ctx.Set("id", id)
pool.ExpectQuery("name: GetQuery :one").WithArgs(database.MustToDBUUID(id)).WillReturnRows(
pgxmock.NewRows([]string{"id", "type", "activeVersion", "latestVersion", "config", "requiredIds"}).
AddRow(database.MustToDBUUID(id), repository.QuerytypeContextFull, int32(1), int32(2), nil, []pgtype.UUID{}),
)
err = cons.GetQuery(ctx, id)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
var res queryapi.Query
err = json.Unmarshal(rec.Body.Bytes(), &res)
assert.NoError(t, err)
assert.EqualExportedValues(t, queryapi.Query{
Id: id,
Type: queryapi.CONTEXTFULL,
ActiveVersion: 1,
LatestVersion: 2,
}, res)
}
func TestUpdateQuery(t *testing.T) {
pool, err := pgxmock.NewPool()
require.NoError(t, err)
cfg := &struct {
serviceconfig.BaseConfig
queryversionsync.QueryVersionSyncConfig
}{}
cfg.DBPool = pool
cfg.DBQueries = repository.New(pool)
mockSQS := queuemock.NewMockSQSClient(t)
cfg.QueueClient = mockSQS
cfg.QueryVersionSyncURL = "here"
cons := queryapi.NewControllers(validator.New(), &queryapi.Services{
QueryUpdate: queryupdate.New(cfg, &queryupdate.Services{
Query: query.New(cfg),
}),
})
av := int32(2)
body := queryapi.QueryUpdate{
ActiveVersion: &av,
}
bodyBytes, err := json.Marshal(body)
assert.NoError(t, err)
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(bodyBytes)))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
ctx := e.NewContext(req, rec)
id := uuid.New()
ctx.Set("id", id)
pool.ExpectQuery("name: GetQuery :one").WithArgs(database.MustToDBUUID(id)).WillReturnRows(
pgxmock.NewRows([]string{"id", "type", "activeVersion", "latestVersion", "config", "requiredIds"}).
AddRow(database.MustToDBUUID(id), repository.QuerytypeContextFull, int32(1), int32(2), []byte(""), []pgtype.UUID{}),
)
pool.ExpectBeginTx(pgx.TxOptions{})
pool.ExpectExec("name: AddActiveQueryVersion :exec").WithArgs(database.MustToDBUUID(id), av).
WillReturnResult(pgxmock.NewResult("", 1))
pool.ExpectCommit()
mockSQS.EXPECT().
SendMessage(
mock.Anything,
mock.MatchedBy(func(in *sqs.SendMessageInput) bool {
return *in.QueueUrl == cfg.QueryVersionSyncURL && *in.MessageBody == fmt.Sprintf("{\"id\":\"%s\"}", id.String())
}),
mock.Anything,
).
Return(&sqs.SendMessageOutput{}, nil)
err = cons.UpdateQuery(ctx, id)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
assert.Empty(t, rec.Body.String())
}
func TestTestQuery(t *testing.T) {
pool, err := pgxmock.NewPool()
require.NoError(t, err)
cfg := &serviceconfig.BaseConfig{}
cfg.DBPool = pool
cfg.DBQueries = repository.New(pool)
docsvc := document.New(cfg)
col := collector.New(cfg)
que := query.New(cfg)
cons := queryapi.NewControllers(validator.New(), &queryapi.Services{
Collector: col,
Query: que,
QueryTest: querytest.New(cfg, &querytest.Services{
Document: docsvc,
Collector: col,
Result: result.New(cfg, &result.Services{
Query: que,
}),
}),
})
doc := document.DocumentSummary{
ID: uuid.New(),
}
params := &result.Process{
QueryID: uuid.New(),
DocumentID: doc.ID,
QueryVersion: int32(3),
}
body := queryapi.QueryTestRequest{
DocumentId: params.DocumentID,
QueryVersion: params.QueryVersion,
}
bodyBytes, err := json.Marshal(body)
assert.NoError(t, err)
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(string(bodyBytes)))
req.Header.Set(echo.HeaderContentType, echo.MIMEApplicationJSON)
rec := httptest.NewRecorder()
ctx := e.NewContext(req, rec)
reqID := database.MustToDBUUID(uuid.New())
pool.ExpectQuery("name: GetQueryWithVersion :one").WithArgs(database.MustToDBUUID(params.QueryID), &params.QueryVersion).WillReturnRows(
pgxmock.NewRows([]string{"id", "type", "activeVersion", "latestVersion", "config", "requiredIds"}).
AddRow(database.MustToDBUUID(params.QueryID), repository.QuerytypeJsonExtractor, int32(1), params.QueryVersion+1, []byte("{\"path\":\"oldkey\"}"), []pgtype.UUID{reqID}),
)
strVal := "{\"mykey\":\"example_value\",\"oldkey\":\"old_value\"}"
pool.ExpectQuery("name: ListQueryRequirementValues :many").WithArgs(database.MustToDBUUID(params.QueryID), &params.QueryVersion, database.MustToDBUUID(params.DocumentID)).
WillReturnRows(
pgxmock.NewRows([]string{"id", "queryId", "type", "value"}).
AddRow(database.MustToDBUUID(uuid.New()), reqID, repository.QuerytypeContextFull, &strVal),
)
pool.ExpectQuery("name: GetActiveQueryConfig :one").WithArgs(database.MustToDBUUID(params.QueryID)).WillReturnRows(
pgxmock.NewRows([]string{"config"}).
AddRow([]byte("{\"path\":\"oldkey\"}")),
)
err = cons.TestQuery(ctx, params.QueryID)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
var res queryapi.QueryTestResponse
err = json.Unmarshal(rec.Body.Bytes(), &res)
assert.NoError(t, err)
assert.EqualExportedValues(t, queryapi.QueryTestResponse{
Value: "old_value",
}, res)
}
+19
View File
@@ -0,0 +1,19 @@
package queryapi
import (
"fmt"
"net/http"
"github.com/labstack/echo/v4"
)
func (s *Controllers) GetStatusByClientId(ctx echo.Context, id ClientID) error {
status, err := s.svc.Client.GetStatusByExternalId(ctx.Request().Context(), id)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unable to find client: %s", err))
}
return ctx.JSON(http.StatusOK, ClientStatusBody{
Status: parseClientStatus(status),
})
}
+62
View File
@@ -0,0 +1,62 @@
package queryapi_test
import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
queryapi "queryorchestration/api/queryAPI"
"queryorchestration/internal/client"
"queryorchestration/internal/database"
"queryorchestration/internal/database/repository"
"queryorchestration/internal/serviceconfig"
"github.com/go-playground/validator/v10"
"github.com/google/uuid"
"github.com/labstack/echo/v4"
"github.com/pashagolub/pgxmock/v3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGetClientStatus(t *testing.T) {
pool, err := pgxmock.NewPool()
require.NoError(t, err)
cfg := &serviceconfig.BaseConfig{}
cfg.DBPool = pool
cfg.DBQueries = repository.New(pool)
cons := queryapi.NewControllers(validator.New(), &queryapi.Services{
Client: client.New(cfg),
})
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(""))
rec := httptest.NewRecorder()
ctx := e.NewContext(req, rec)
clientId := uuid.New()
id := "clientid"
pool.ExpectQuery("name: GetClientByExternalId :one").WithArgs(id).WillReturnRows(
pgxmock.NewRows([]string{"id", "externalId", "name", "can_sync"}).
AddRow(database.MustToDBUUID(clientId), id, "name", true),
)
pool.ExpectQuery("name: IsClientSynced :one").WithArgs(database.MustToDBUUID(clientId)).WillReturnRows(
pgxmock.NewRows([]string{"issynced"}).
AddRow(true),
)
err = cons.GetStatusByClientId(ctx, id)
assert.NoError(t, err)
assert.Equal(t, http.StatusOK, rec.Code)
var res queryapi.ClientStatusBody
err = json.Unmarshal(rec.Body.Bytes(), &res)
assert.NoError(t, err)
assert.EqualExportedValues(t, queryapi.ClientStatusBody{
Status: queryapi.INSYNC,
}, res)
}