Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into devops/deployment
Browse files Browse the repository at this point in the history
  • Loading branch information
dvrkni committed Nov 6, 2023
2 parents 72bbd9f + 7b419d1 commit cf31c11
Show file tree
Hide file tree
Showing 10 changed files with 67 additions and 41 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# GoLand IDE
.idea/

# Fleet IDE
.fleet/

# Compiled Go programs and executables
*.exe
*.out
Expand Down
51 changes: 34 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,65 @@
# Updating the API and generating code
[![Build Status](https://github.com/il-blood-donation-info/blood-donation-backend/actions/workflows/on_push.yml/badge.svg)](https://github.com/il-blood-donation-info/blood-donation-backend/actions/workflows/on_push.yml)

# Blood Donation Backend Service
The project is used to collect and persist blood donation stations, and offers an API to manage stations availability.
It also allows querying for the stations.

View OpenAPI definition using Swagger UI [here](https://generator.swagger.io/?url=https://raw.githubusercontent.com/il-blood-donation-info/blood-donation-backend/main/pkg/api/openapi.yaml#/).

## Updating the API and generating code
In order to modify the API, edit the api/openapi.yaml file. Then, run the following commands to generate the code:
```bash
oapi-codegen -config api/api.cfg.yaml api/openapi.yaml
oapi-codegen -config pkg/api/api.cfg.yaml pkg/api/openapi.yaml
```

## Installing the code-gen dependency
If `oapi-codegen` isn't available, install the code-gen dependency:
```bash
go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@latest
```

# Creating a Database
## Setting development environment

### Creating a Database
```postgresql
CREATE DATABASE bloodinfo;
CREATE USER mada WITH PASSWORD <your password>; # change this
GRANT ALL PRIVILEGES ON DATABASE bloodinfo TO mada;
```

# Running the server
### Running the server
```bash
export DB_HOST=localhost
export DB_PORT=5432
export DB_USER=mada
export DB_NAME=bloodinfo
export DB_PASSWORD= # your password
go run cert/tls-self-signed-cert.go
go run main.go
go run cmd/cert/tls-self-signed-cert.go
go run cmd/server/main.go
```

# Running using docker-compose
## Running using docker-compose

## Building the image and running the containers
### Building the image and running the containers
Edit or change the db.env file to your liking. Then, run the following command:

```bash
docker-compose --env_file db.env up --build
docker-compose --env-file db.env up --build
```
## Stopping the containers

Note this generates a self-signed cert & key during the build, separate from anything you might have in cert.pem file.
So once it built, you want to run this to get the same ./cert.pem:
```
docker run blood-donation-backend_blood-info /bin/cat cert.pem > cert.pem
```

### Stopping the containers
```bash
docker-compose --env_file db.env down --remove-orphans --volumes --rmi local
docker-compose --env-file db.env down --remove-orphans --volumes --rmi local
```

# Testing the API
## Testing the API

## Creating a user
### Creating a user
```bash
curl --cacert ./cert.pem -X POST https://localhost:8443/users -H "Content-Type: application/json" -d '{"description": "User description",
"email": "[email protected]",
Expand All @@ -53,7 +70,7 @@ curl --cacert ./cert.pem -X POST https://localhost:8443/users -H "Content-Type:
"role": "Admin"}'
```

## Getting a user
### Getting a user
```bash
curl --cacert ./cert.pem -s -X GET https://localhost:8443/users | jq
[
Expand Down Expand Up @@ -88,5 +105,5 @@ EOF
# Use echo to pass the commands to psql
echo "$TEST_DB_COMMANDS" | psql -U postgres

go test ./scraper/... -v
```
go test ./pkg/scraper/... -v
```
4 changes: 3 additions & 1 deletion cmd/server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ FROM golang:1.20-alpine as build
WORKDIR /app

# Copy the source code into the container
COPY ../.. .
COPY . .

# Build the Go application
RUN go build -o blood-info cmd/server/main.go
Expand All @@ -20,6 +20,8 @@ COPY --from=build /app/blood-info blood-info
COPY --from=build /app/tls-self-signed-cert tls-self-signed-cert

# FIXME: This is a hack to get the self-signed certificate to work. There must be a better way.
# FIXME: This makes the secret `key.pem` also part of the image; uploading the image anywhere would leak it!
# FIXME: any prior `cert.pem` & `key.pem` you had in current dir are *also* leaked in the COPY layer.
RUN ./tls-self-signed-cert

RUN addgroup -S gouser && adduser -S gouser -G gouser
Expand Down
9 changes: 8 additions & 1 deletion cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/il-blood-donation-info/blood-donation-backend/pkg/api"
"github.com/il-blood-donation-info/blood-donation-backend/server"
middleware "github.com/oapi-codegen/nethttp-middleware"
"github.com/rs/cors"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"log"
Expand Down Expand Up @@ -51,9 +52,15 @@ func main() {
swagger.Servers = nil

strictBloodInfoServer := server.NewStrictBloodInfoServer(db)
strictHandler := api.NewStrictHandler(strictBloodInfoServer, nil)

// We can add 2 kinds of middlewares:
// - "strict" middlewares here deal in per-request generated `api` types.
// - r.Use() chi middlewares below deal in de-facto standard `http.Handler`.
strictMiddlewares := []api.StrictMiddlewareFunc{}
strictHandler := api.NewStrictHandler(strictBloodInfoServer, strictMiddlewares)

r := chi.NewRouter()
r.Use(cors.Default().Handler) // Tell browsers cross-origin requests OK from any domain.
r.Use(middleware.OapiRequestValidator(swagger))
api.HandlerFromMux(strictHandler, r)
s := &http.Server{
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/go-chi/chi/v5 v5.0.10
github.com/oapi-codegen/nethttp-middleware v1.0.1
github.com/oapi-codegen/runtime v1.0.0
github.com/rs/cors v1.10.1
gorm.io/driver/postgres v1.5.3
gorm.io/gorm v1.25.5
)
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo=
github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/api.cfg.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package: api
output: api/api.gen.go
output: pkg/api/api.gen.go
generate:
embedded-spec: true
strict-server: true
Expand Down
6 changes: 3 additions & 3 deletions pkg/api/api.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkg/scraper/mada_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func setupDatabase() *gorm.DB {
if err != nil {
panic("failed to connect to test database")
}

err = db.AutoMigrate(&api.User{}, &api.Station{}, &api.StationStatus{}, &api.StationSchedule{})
if err != nil {
log.Fatalf("Failed to migrate... %+v", err)
Expand Down Expand Up @@ -127,7 +128,6 @@ func TestScrapeMada(t *testing.T) {
if len(scheduledYesterday) > 0 {
t.Fatal("no yesterday dates should be present in schedule")
}

todaySchedule := schedule.FilterByDate(today)
if len(todaySchedule) != 5 {
t.Fatal(fmt.Sprintf("today should have 5 schedule points, has: %d", len(todaySchedule)))
Expand Down
28 changes: 11 additions & 17 deletions pkg/scraper/schedule_datawriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ func (p ScheduleDataWriter) processDonationDetails(tx *gorm.DB, donationDetails
}
station.Address = stationAddress

if err := tx.Save(&station).Error; err != nil {
log.Printf("Error while saving station: %v", err)
return err
}

if !p.isDatePassed(donation.DateDonation) {
var schedule = api.StationSchedule{}
if err := tx.FirstOrInit(&schedule, api.StationSchedule{
Expand All @@ -65,24 +70,13 @@ func (p ScheduleDataWriter) processDonationDetails(tx *gorm.DB, donationDetails
schedule.StationStatus = &[]api.StationStatus{{IsOpen: true}}
}

if station.StationSchedule == nil {
station.StationSchedule = &[]api.StationSchedule{schedule}
} else {
*station.StationSchedule = append(*station.StationSchedule, schedule)
if err := tx.Save(&schedule).Error; err != nil {
log.Printf("Error while saving schedule: %v", err)
return err
}
}

//Gorm save station, schedule, and status at this point. That's why we are taking schedule. ID only after.
if err := tx.Save(&station).Error; err != nil {
log.Printf("Error while saving station: %v", err)
return err
}

if station.StationSchedule != nil {
for _, schedule := range *station.StationSchedule {
if schedule.Id != nil {
schedulesIdsMap[*schedule.Id] = true
}
if schedule.Id != nil {
schedulesIdsMap[*schedule.Id] = true
}
}
}
Expand All @@ -93,7 +87,7 @@ func (p ScheduleDataWriter) processDonationDetails(tx *gorm.DB, donationDetails
}

var otherSchedules []api.StationSchedule
if err := tx.Not("id", schedulesIds).Where("DATE(date) >= CURRENT_DATE").Find(&otherSchedules).Error; err != nil {
if err := tx.Not("id", schedulesIds).Where("DATE(date) >= ?", p.SinceTime.Format("2006-01-02")).Find(&otherSchedules).Error; err != nil {
log.Printf("Error while searching other schedules: %v", err)
return err
}
Expand Down

0 comments on commit cf31c11

Please sign in to comment.