Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add spacetastic endpoints DOC-1138 #16

Merged
merged 14 commits into from
Sep 6, 2024
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
- main

env:
DB_VERSION: 1.0.0
DB_VERSION: 1.1.0

concurrency:
group: ci-${{ github.ref }}
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright (c) Spectro Cloud
# SPDX-License-Identifier: MPL-2.0

FROM golang:1.21.7-alpine3.19 as builder
FROM golang:1.23.0-alpine3.20 as builder
WORKDIR /go/src/app
COPY . .
RUN go build -o /go/bin/app && \
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.PHONY: license


VERSION:=1.0.0
VERSION:=1.1.0

build:
go build -o hello-universe-api
Expand Down
33 changes: 30 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[![semantic-release: angular](https://img.shields.io/badge/semantic--release-angular-e10079?logo=semantic-release)](https://github.com/semantic-release/semantic-release)
![Coverage](https://img.shields.io/badge/Coverage-54.2%25-yellow)
![Coverage](https://img.shields.io/badge/Coverage-42.6%25-yellow)

# Hello Universe API

Expand All @@ -17,13 +17,40 @@ The [Hello Universe](https://github.com/spectrocloud/hello-universe) app include

A Postman collection is available to help you explore the API. Review the [Postman collection](./tests/postman_collection.json) to get started.

# Prerequisites
Ensure [Docker Desktop](https://www.docker.com/products/docker-desktop/) on your local machine is available.

- Use the following command and ensure you receive an output displaying the version number.
```shell
docker version
```

Alternatively, you can install [Podman](https://podman.io/docs/installation).

- If you are not using a Linux operating system, create and start the Podman Machine in your local environment. Otherwise, skip this step.
```shell
podman machine init
podman machine start
```
- Use the following command and ensure you receive an output displaying the installation information.
```shell
podman info
```

# Usage

The quickest method to start the API server locally is by using the Docker image.

```shell
docker pull ghcr.io/spectrocloud/hello-universe-api:1.0.12
docker run -p 3000:3000 ghcr.io/spectrocloud/hello-universe-api:1.0.12
docker pull ghcr.io/spectrocloud/hello-universe-api:1.1.0
docker run -p 3000:3000 ghcr.io/spectrocloud/hello-universe-api:1.1.0
```

If you choose Podman, you can use the following commands.

```shell
podman pull ghcr.io/spectrocloud/hello-universe-api:1.1.0
podman run -p 3000:3000 ghcr.io/spectrocloud/hello-universe-api:1.1.0
```

To start the API server you must have connectivity to a Postgres instance. Use [environment variables](#environment-variables) to customize the API server start parameters.
Expand Down
60 changes: 45 additions & 15 deletions endpoints/counterRoute.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ func NewCounterHandlerContext(db *sqlx.DB, ctx context.Context, authorization bo
}

func (route *CounterRoute) CounterHTTPHandler(writer http.ResponseWriter, request *http.Request) {
log.Debug().Msg("POST request received. Incrementing counter.")
page := request.PathValue("page")

writer.Header().Set("Content-Type", "application/json")
writer.Header().Set("Access-Control-Allow-Origin", "*")
writer.Header().Set("Access-Control-Allow-Headers", "*")
var payload []byte

if route.authorization && request.Method != "OPTIONS" {
if route.Authorization && request.Method != "OPTIONS" {
validation := internal.ValidateToken(request.Header.Get("Authorization"))
if !validation {
log.Info().Msg("Invalid token.")
Expand All @@ -39,15 +40,16 @@ func (route *CounterRoute) CounterHTTPHandler(writer http.ResponseWriter, reques

switch request.Method {
case "POST":
value, err := route.postHandler(request)
log.Debug().Msg("POST request received. Incrementing counter.")
value, err := route.postHandler(request, page)
if err != nil {
log.Debug().Msg("Error incrementing counter.")
http.Error(writer, "Error incrementing counter.", http.StatusInternalServerError)
}
writer.WriteHeader(http.StatusCreated)
payload = value
case "GET":
value, err := route.getHandler(request)
value, err := route.getHandler(page)
if err != nil {
log.Debug().Msg("Error getting counter value.")
http.Error(writer, "Error getting counter value.", http.StatusInternalServerError)
Expand All @@ -70,27 +72,27 @@ func (route *CounterRoute) CounterHTTPHandler(writer http.ResponseWriter, reques
}

// postHandler increments the counter in the database.
func (route *CounterRoute) postHandler(r *http.Request) ([]byte, error) {
func (route *CounterRoute) postHandler(r *http.Request, page string) ([]byte, error) {
currentTime := time.Now().UTC()
ua := useragent.Parse(r.UserAgent())
browser := ua.Name
os := ua.OS
transaction, err := route.DB.BeginTx(route.ctx, nil)
transaction, err := route.DB.BeginTx(route.Ctx, nil)
if err != nil {
log.Error().Err(err).Msg("Error beginning transaction.")
return []byte{}, err
}
sqlQuery := `INSERT INTO counter(date,browser,os) VALUES ($1, $2, $3)`
_, err = transaction.ExecContext(route.ctx, sqlQuery, currentTime, browser, os)
sqlQuery := `INSERT INTO counter(page, date, browser, os) VALUES ($1, $2, $3, $4)`
_, err = transaction.ExecContext(route.Ctx, sqlQuery, page, currentTime, browser, os)
if err != nil {
log.Error().Err(err).Msg("Error inserting counter value.")
log.Debug().Msgf("SQL query: %s", sqlQuery)
return []byte{}, err
}
log.Info().Msg("Counter incremented in database.")
getNewCountQuery := `SELECT COUNT(*) AS total FROM counter`
getNewCountQuery := `SELECT COUNT(*) AS total FROM counter WHERE page = $1`
var databaseTotal sql.NullInt64
result := transaction.QueryRowContext(route.ctx, getNewCountQuery)
result := transaction.QueryRowContext(route.Ctx, getNewCountQuery, page)
err = result.Scan(&databaseTotal)
if err != nil {
log.Error().Err(err).Msg("Error scanning counter value.")
Expand All @@ -100,7 +102,7 @@ func (route *CounterRoute) postHandler(r *http.Request) ([]byte, error) {
log.Error().Err(err).Msg("Counter value is null.")
return []byte{}, err
}
counterSummary := counterSummary{Total: databaseTotal.Int64}
counterSummary := CounterSummary{Total: databaseTotal.Int64}
err = transaction.Commit()
if err != nil {
log.Error().Err(err).Msg("Error committing transaction.")
Expand All @@ -120,13 +122,22 @@ func (route *CounterRoute) postHandler(r *http.Request) ([]byte, error) {
}

// getHandler returns the current counter value from the database as a JSON object.
func (route *CounterRoute) getHandler(r *http.Request) ([]byte, error) {
func (route *CounterRoute) getHandler(page string) ([]byte, error) {
if page != "" {
return route.getHandlerForPage(page)
}

return route.getHandlerAllPages()
}

// getHandlerAllPages returns the current counter value for all pages from the database as a JSON object.
func (route *CounterRoute) getHandlerAllPages() ([]byte, error) {
sqlQuery := `SELECT COUNT(*) AS total FROM counter`
var counterSummary counterSummary
err := route.DB.GetContext(route.ctx, &counterSummary, sqlQuery)
var counterSummary CounterSummary
err := route.DB.GetContext(route.Ctx, &counterSummary, sqlQuery)
if err != nil {
log.Error().Err(err).Msg("Error getting counter value.")
log.Debug().Msgf("SQL query: %s", sqlQuery)
log.Info().Msgf("SQL query: %s", sqlQuery)
return []byte{}, err
}
log.Info().Msg("Counter value retrieved from database.")
Expand All @@ -137,3 +148,22 @@ func (route *CounterRoute) getHandler(r *http.Request) ([]byte, error) {
}
return payload, nil
}

// getHandlerForPage returns the current counter value for a single page from the database as a JSON object.
func (route *CounterRoute) getHandlerForPage(page string) ([]byte, error) {
sqlQuery := `SELECT COUNT(*) AS total FROM counter WHERE page = $1`
var counterSummary CounterSummary
err := route.DB.GetContext(route.Ctx, &counterSummary, sqlQuery, page)
addetz marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
log.Error().Err(err).Msg("Error getting counter value.")
log.Info().Msgf("SQL query: %s", sqlQuery)
return []byte{}, err
}
log.Info().Msgf("Counter value retrieved from database for page %s", page)
payload, err := json.MarshalIndent(counterSummary, "", " ")
if err != nil {
log.Error().Err(err).Msg("Error marshalling counterSummary struct into JSON.")
return []byte{}, err
}
return payload, nil
}
Loading
Loading