diff --git a/Dockerfile_migrations b/Dockerfile_db similarity index 73% rename from Dockerfile_migrations rename to Dockerfile_db index 252ed21..967d446 100644 --- a/Dockerfile_migrations +++ b/Dockerfile_db @@ -12,3 +12,6 @@ ENV TZ=America/Argentina/Buenos_Aires # Make HTTP request to /migrations/run/v1 endpoint CMD ["sh", "-c", "curl -X POST http://ahbcc:${API_PORT}/migrations/run/v1"] +# Make HTTP request to /criteria/init/v1 endpoint +CMD ["sh", "-c", "curl -X POST http://ahbcc:${API_PORT}/criteria/init/v1"] + diff --git a/cmd/api/main.go b/cmd/api/main.go index f245d3a..578904f 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -63,6 +63,8 @@ func main() { scrapperEnqueueCriteria := scrapper.MakeEnqueueCriteria(httpClient, os.Getenv("ENQUEUE_CRITERIA_API_URL")) enqueueCriteria := criteria.MakeEnqueue(selectCriteriaByID, selectLastDayExecutedByCriteriaID, selectExecutionsByStatuses, scrapperEnqueueCriteria) + initCriteria := criteria.MakeInit(selectExecutionsByStatuses, enqueueCriteria) + /* --- Router --- */ log.Info(ctx, "Initializing router...") router := http.NewServeMux() @@ -70,6 +72,7 @@ func main() { router.HandleFunc("POST /migrations/run/v1", migrations.RunHandlerV1(runMigrations)) router.HandleFunc("POST /tweets/v1", tweets.InsertHandlerV1(insertTweets)) router.HandleFunc("POST /criteria/{criteria_id}/enqueue/v1", criteria.EnqueueHandlerV1(enqueueCriteria)) + router.HandleFunc("POST /criteria/init/v1", criteria.InitHandlerV1(initCriteria)) log.Info(ctx, "Router initialized!") /* --- Server --- */ diff --git a/cmd/api/search/criteria/errors.go b/cmd/api/search/criteria/errors.go index 8d65336..c0f33be 100644 --- a/cmd/api/search/criteria/errors.go +++ b/cmd/api/search/criteria/errors.go @@ -7,6 +7,7 @@ const ( InvalidQueryParameterFormat string = "Invalid query parameter format" FailedToEnqueueCriteria string = "Failed to execute enqueue criteria" ExecutionWithSameCriteriaIDAlreadyEnqueued string = "An execution with the same criteria id is already enqueued" + FailedToExecuteInitCriteria string = "Failed to execute init criteria" ) var ( diff --git a/cmd/api/search/criteria/handler.go b/cmd/api/search/criteria/handler.go index a3154f6..9f78e5b 100644 --- a/cmd/api/search/criteria/handler.go +++ b/cmd/api/search/criteria/handler.go @@ -8,7 +8,7 @@ import ( "ahbcc/internal/log" ) -// EnqueueHandlerV1 HTTP Handler of the endpoint /criteria/enqueue/v1 +// EnqueueHandlerV1 HTTP Handler of the endpoint /criteria/{criteria_id}/enqueue/v1 func EnqueueHandlerV1(enqueueCriteria Enqueue) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -47,3 +47,21 @@ func EnqueueHandlerV1(enqueueCriteria Enqueue) http.HandlerFunc { _, _ = w.Write([]byte("Criteria successfully sent to enqueue")) } } + +// InitHandlerV1 HTTP Handler of the endpoint /criteria/init/v1 +func InitHandlerV1(init Init) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + err := init(ctx) + if err != nil { + log.Error(ctx, err.Error()) + http.Error(w, FailedToExecuteInitCriteria, http.StatusInternalServerError) + return + } + + log.Info(ctx, "Criteria successfully initialized and enqueued") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("Criteria successfully initialized and enqueued")) + } +} diff --git a/cmd/api/search/criteria/handler_test.go b/cmd/api/search/criteria/handler_test.go index 0afe91c..8e2796f 100644 --- a/cmd/api/search/criteria/handler_test.go +++ b/cmd/api/search/criteria/handler_test.go @@ -105,3 +105,35 @@ func TestEnqueueHandlerV1_failsWhenEnqueueCriteriaThrowsError(t *testing.T) { assert.Equal(t, want, got) } } + +func TestInitHandlerV1_success(t *testing.T) { + mockInit := criteria.MockInit(nil) + + mockResponseWriter := httptest.NewRecorder() + mockRequest, _ := http.NewRequestWithContext(context.Background(), http.MethodPost, "/criteria/init", http.NoBody) + + handlerV1 := criteria.InitHandlerV1(mockInit) + + handlerV1(mockResponseWriter, mockRequest) + + want := http.StatusOK + got := mockResponseWriter.Result().StatusCode + + assert.Equal(t, want, got) +} + +func TestInitHandlerV1_failsWhenInitThrowsError(t *testing.T) { + mockInit := criteria.MockInit(errors.New("failed while executing init criteria")) + + mockResponseWriter := httptest.NewRecorder() + mockRequest, _ := http.NewRequestWithContext(context.Background(), http.MethodPost, "/criteria/init", http.NoBody) + + handlerV1 := criteria.InitHandlerV1(mockInit) + + handlerV1(mockResponseWriter, mockRequest) + + want := http.StatusInternalServerError + got := mockResponseWriter.Result().StatusCode + + assert.Equal(t, want, got) +} diff --git a/cmd/api/search/criteria/init.go b/cmd/api/search/criteria/init.go new file mode 100644 index 0000000..3966977 --- /dev/null +++ b/cmd/api/search/criteria/init.go @@ -0,0 +1,31 @@ +package criteria + +import ( + "context" + + "ahbcc/internal/log" +) + +// Init retrieves all the criteria in a 'PENDING' or 'IN PROGRESS' state and executes an enqueue of each one +type Init func(ctx context.Context) error + +// MakeInit creates a new Init +func MakeInit(selectExecutionsByStatuses SelectExecutionsByStatuses, enqueue Enqueue) Init { + return func(ctx context.Context) error { + executionsDAO, err := selectExecutionsByStatuses(ctx, []string{PendingStatus, InProgressStatus}) + if err != nil { + log.Error(ctx, err.Error()) + return FailedToExecuteSelectExecutionsByStatuses + } + + for _, execution := range executionsDAO { + err = enqueue(ctx, execution.SearchCriteriaID, true) + if err != nil { + log.Error(ctx, err.Error()) + return FailedToExecuteEnqueueCriteria + } + } + + return nil + } +} diff --git a/cmd/api/search/criteria/init_test.go b/cmd/api/search/criteria/init_test.go new file mode 100644 index 0000000..271a7ba --- /dev/null +++ b/cmd/api/search/criteria/init_test.go @@ -0,0 +1,48 @@ +package criteria_test + +import ( + "context" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + + "ahbcc/cmd/api/search/criteria" +) + +func TestInit_success(t *testing.T) { + mockExecutionsDAO := criteria.MockExecutionsDAO() + mockSelectExecutionsByStatuses := criteria.MockSelectExecutionsByStatuses(mockExecutionsDAO, nil) + mockEnqueue := criteria.MockEnqueue(nil) + + init := criteria.MakeInit(mockSelectExecutionsByStatuses, mockEnqueue) + + got := init(context.Background()) + + assert.Nil(t, got) +} + +func TestInit_failsWhenSelectExecutionsByStatusesThrowsError(t *testing.T) { + mockSelectExecutionsByStatuses := criteria.MockSelectExecutionsByStatuses(nil, errors.New("failed while executing select executions by statuses")) + mockEnqueue := criteria.MockEnqueue(nil) + + init := criteria.MakeInit(mockSelectExecutionsByStatuses, mockEnqueue) + + want := criteria.FailedToExecuteSelectExecutionsByStatuses + got := init(context.Background()) + + assert.Equal(t, want, got) +} + +func TestInit_failsWhenEnqueueThrowsError(t *testing.T) { + mockExecutionsDAO := criteria.MockExecutionsDAO() + mockSelectExecutionsByStatuses := criteria.MockSelectExecutionsByStatuses(mockExecutionsDAO, nil) + mockEnqueue := criteria.MockEnqueue(errors.New("failed while executing enqueue")) + + init := criteria.MakeInit(mockSelectExecutionsByStatuses, mockEnqueue) + + want := criteria.FailedToExecuteEnqueueCriteria + got := init(context.Background()) + + assert.Equal(t, want, got) +} diff --git a/cmd/api/search/criteria/mocks.go b/cmd/api/search/criteria/mocks.go index a0854fc..132bbe0 100644 --- a/cmd/api/search/criteria/mocks.go +++ b/cmd/api/search/criteria/mocks.go @@ -30,6 +30,13 @@ func MockEnqueue(err error) Enqueue { } } +// MockInit mocks Init function +func MockInit(err error) Init { + return func(ctx context.Context) error { + return err + } +} + // MockCriteriaDAO mocks a criteria.DAO func MockCriteriaDAO() DAO { return DAO{ diff --git a/compose.yml b/compose.yml index e9b1da9..27b5c32 100644 --- a/compose.yml +++ b/compose.yml @@ -29,12 +29,12 @@ services: timeout: 10s retries: 5 - migrations: + db_initializer: image: alpine:latest build: context: . - dockerfile: Dockerfile_migrations - container_name: migrations + dockerfile: Dockerfile_db + container_name: db_initializer environment: API_PORT: ${APP_INTERNAL_PORT:-4001} env_file: