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

Next generation (big PR) #4

Merged
merged 3 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,33 @@
# api.acmcsuf.com

ACM at CSUF club API for managing events, announcements, forms, and other services!

## Develop

### Start API server

```sh
go run cmd/api/main.go
```

### Generate code

```sh
go generate ./...
```

### Run tests

```sh
go test ./...
```

### Format code

```sh
go fmt ./...
```

---

Developed with 💚 by [**@acmcsufoss**](https://github.com/acmcsufoss)
80 changes: 80 additions & 0 deletions cmd/api/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package main

import (
"context"
"database/sql"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"

"github.com/acmcsufoss/api.acmcsuf.com/internal/api"
"github.com/acmcsufoss/api.acmcsuf.com/internal/db/sqlite"
)

func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// Set up signal handling for graceful shutdown.
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM)
go func() {
<-signalChan
log.Println("Shutting down the server...")
cancel()
}()

// Set up the database connection.
uri, ok := os.LookupEnv("DATABASE_URL")
if !ok {
log.Fatal("DATABASE_URL must be set")
}

d, err := sql.Open("sqlite", uri)
if err != nil {
log.Fatalf("Error opening SQLite database: %v", err)
}

// if err := sqlite.Migrate(ctx, db); err != nil {
// return nil, errors.Wrap(err, "cannot migrate sqlite db")
// }
//
// return sqliteStore{
// q: sqlite.New(db),
// db: db,
// ctx: ctx,
// }, nil

q := sqlite.New(d)
if err != nil {
log.Fatalf("Error creating SQLite store: %v", err)
}
defer func() {
if err := d.Close(); err != nil {
log.Fatalf("Error closing SQLite store: %v", err)
}
}()

// Initialize and start the HTTP server.
handler := api.New(q)
port, ok := os.LookupEnv("PORT")
if !ok {
port = "8080"
}

serverAddr := fmt.Sprintf(":%s", port)
go func() {
fmt.Printf("Server started on http://127.0.0.1%s\n", serverAddr)
if err := http.ListenAndServe(serverAddr, handler); err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
}()

// Wait for shutdown signal.
<-ctx.Done()

log.Println("Server shut down.")
}
22 changes: 22 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module github.com/acmcsufoss/api.acmcsuf.com

go 1.21.3

require (
github.com/pkg/errors v0.9.1
github.com/swaggest/openapi-go v0.2.45
github.com/swaggest/rest v0.2.61
github.com/swaggest/swgui v1.8.0
github.com/swaggest/usecase v1.3.1
)

require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/go-chi/chi/v5 v5.0.10 // indirect
github.com/santhosh-tekuri/jsonschema/v3 v3.1.0 // indirect
github.com/swaggest/form/v5 v5.1.1 // indirect
github.com/swaggest/jsonschema-go v0.3.64 // indirect
github.com/swaggest/refl v1.3.0 // indirect
github.com/vearutop/statigz v1.4.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
57 changes: 57 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/bool64/dev v0.2.25/go.mod h1:iJbh1y/HkunEPhgebWRNcs8wfGq7sjvJ6W5iabL8ACg=
github.com/bool64/dev v0.2.32 h1:DRZtloaoH1Igky3zphaUHV9+SLIV2H3lsf78JsJHFg0=
github.com/bool64/dev v0.2.32/go.mod h1:iJbh1y/HkunEPhgebWRNcs8wfGq7sjvJ6W5iabL8ACg=
github.com/bool64/shared v0.1.5 h1:fp3eUhBsrSjNCQPcSdQqZxxh9bBwrYiZ+zOKFkM0/2E=
github.com/bool64/shared v0.1.5/go.mod h1:081yz68YC9jeFB3+Bbmno2RFWvGKv1lPKkMP6MHJlPs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc=
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/santhosh-tekuri/jsonschema/v3 v3.1.0 h1:levPcBfnazlA1CyCMC3asL/QLZkq9pa8tQZOH513zQw=
github.com/santhosh-tekuri/jsonschema/v3 v3.1.0/go.mod h1:8kzK2TC0k0YjOForaAHdNEa7ik0fokNa2k30BKJ/W7Y=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/swaggest/assertjson v1.9.0 h1:dKu0BfJkIxv/xe//mkCrK5yZbs79jL7OVf9Ija7o2xQ=
github.com/swaggest/assertjson v1.9.0/go.mod h1:b+ZKX2VRiUjxfUIal0HDN85W0nHPAYUbYH5WkkSsFsU=
github.com/swaggest/form/v5 v5.1.1 h1:ct6/rOQBGrqWUQ0FUv3vW5sHvTUb31AwTUWj947N6cY=
github.com/swaggest/form/v5 v5.1.1/go.mod h1:X1hraaoONee20PMnGNLQpO32f9zbQ0Czfm7iZThuEKg=
github.com/swaggest/jsonschema-go v0.3.64 h1:HyB41fkA4XP0BZkqWfGap5i2JtRHQGXG/21dGDPbyLM=
github.com/swaggest/jsonschema-go v0.3.64/go.mod h1:DYuKqdpms/edvywsX6p1zHXCZkdwB28wRaBdFCe3Duw=
github.com/swaggest/openapi-go v0.2.45 h1:LOMAEleKVLg4E86lSCyioJK7ltjWRx50AaP4LZIbJ+Q=
github.com/swaggest/openapi-go v0.2.45/go.mod h1:/ykzNtS1ZO7X43OnEtyisMktxCiawQLyGd08rkjV68U=
github.com/swaggest/refl v1.3.0 h1:PEUWIku+ZznYfsoyheF97ypSduvMApYyGkYF3nabS0I=
github.com/swaggest/refl v1.3.0/go.mod h1:3Ujvbmh1pfSbDYjC6JGG7nMgPvpG0ehQL4iNonnLNbg=
github.com/swaggest/rest v0.2.61 h1:K2cc3yGTX5i6dkxszS8lH9DmRlqDRl2XWaRxxBpP/Jk=
github.com/swaggest/rest v0.2.61/go.mod h1:nQGcwz5pD3LGxMXAz0swBTSFHxAWgAh3QnMh7Q4lRvo=
github.com/swaggest/swgui v1.8.0 h1:dPu8TsYIOraaObAkyNdoiLI8mu7nOqQ6SU7HOv254rM=
github.com/swaggest/swgui v1.8.0/go.mod h1:YBaAVAwS3ndfvdtW8A4yWDJpge+W57y+8kW+f/DqZtU=
github.com/swaggest/usecase v1.3.1 h1:JdKV30MTSsDxAXxkldLNcEn8O2uf565khyo6gr5sS+w=
github.com/swaggest/usecase v1.3.1/go.mod h1:cae3lDd5VDmM36OQcOOOdAlEDg40TiQYIp99S9ejWqA=
github.com/vearutop/statigz v1.4.0 h1:RQL0KG3j/uyA/PFpHeZ/L6l2ta920/MxlOAIGEOuwmU=
github.com/vearutop/statigz v1.4.0/go.mod h1:LYTolBLiz9oJISwiVKnOQoIwhO1LWX1A7OECawGS8XE=
github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
51 changes: 51 additions & 0 deletions internal/api/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package api

import (
"github.com/swaggest/openapi-go/openapi3"
"github.com/swaggest/rest/response/gzip"
"github.com/swaggest/rest/web"
swgui "github.com/swaggest/swgui/v5emb"

"github.com/acmcsufoss/api.acmcsuf.com/internal/api/services"
"github.com/acmcsufoss/api.acmcsuf.com/internal/api/services/events"
"github.com/acmcsufoss/api.acmcsuf.com/internal/api/services/resources"
"github.com/acmcsufoss/api.acmcsuf.com/internal/db/sqlite"
)

func New(q *sqlite.Queries) *web.Service {
s := web.NewService(openapi3.NewReflector())

// Init API documentation schema.
s.OpenAPISchema().SetTitle("Basic Example")
s.OpenAPISchema().SetDescription("This app showcases a trivial REST API.")
s.OpenAPISchema().SetVersion("v0.0.1")

// Setup middlewares.
s.Wrap(
gzip.Middleware, // Response compression with support for direct gzip pass through.
)

// Add use case handler to router.
// s.Get("/hello/{name}", helloWorld())
useAll(s, q)

// Swagger UI endpoint at /docs.
s.Docs("/docs", swgui.New)

return s
}

func useAll(s *web.Service, q *sqlite.Queries) {
use("/resources", resources.New(q), s)
use("/events", events.New(q), s)
}

func use(path string, s services.Service, ss *web.Service) {
ss.Get(path, s.Resources())
ss.Post(path, s.PostResources())
ss.Post(path, s.BatchPostResources())
ss.Get(path+"/{id}", s.Resource())
ss.Post(path+"/{id}", s.PostResource())
ss.Post(path+"/{id}", s.BatchPostResource())
ss.Delete(path+"/{id}", s.DeleteResource())
}
47 changes: 47 additions & 0 deletions internal/api/services/events/service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package events

import (
"github.com/swaggest/usecase"

"github.com/acmcsufoss/api.acmcsuf.com/internal/api/services"
"github.com/acmcsufoss/api.acmcsuf.com/internal/db/sqlite"
)

var _ services.Service = EventsService{}

type EventsService struct {
q *sqlite.Queries
}

func New(q *sqlite.Queries) *EventsService {
return &EventsService{q}
}

func (s EventsService) Resources() usecase.IOInteractor {
panic("implement me")
// s.q.GetResourceList(context.TODO(), "")
}

func (s EventsService) PostResources() usecase.IOInteractor {
panic("implement me")
}

func (s EventsService) BatchPostResources() usecase.IOInteractor {
panic("implement me")
}

func (s EventsService) Resource() usecase.IOInteractor {
panic("implement me")
}

func (s EventsService) PostResource() usecase.IOInteractor {
panic("implement me")
}

func (s EventsService) BatchPostResource() usecase.IOInteractor {
panic("implement me")
}

func (s EventsService) DeleteResource() usecase.IOInteractor {
panic("implement me")
}
29 changes: 29 additions & 0 deletions internal/api/services/interfaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package services

import (
"github.com/swaggest/usecase"
)

// Service is the interface of API endpoints for a resource service.
type Service interface {
// Resources gets a list of paginated resource resources.
Resources() usecase.IOInteractor

// PostResources creates a new resource resource.
PostResources() usecase.IOInteractor

// BatchPostResources creates multiple new resource resources.
BatchPostResources() usecase.IOInteractor

// Resource gets a single resource resource.
Resource() usecase.IOInteractor

// PostResource creates a new resource resource.
PostResource() usecase.IOInteractor

// BatchPostResource creates multiple new resource resources.
BatchPostResource() usecase.IOInteractor

// DeleteResource deletes a single resource resource.
DeleteResource() usecase.IOInteractor
}
Loading
Loading