├── README.md ├── api └── README.md ├── backend ├── .gitignore ├── Dockerfile ├── README.md ├── go.mod ├── go.sum ├── main.go ├── patient │ ├── dao.go │ ├── handlerNotes.go │ └── handlerPatients.go ├── patientdb │ ├── init.go │ ├── models │ │ ├── boil_main_test.go │ │ ├── boil_queries.go │ │ ├── boil_queries_test.go │ │ ├── boil_suites_test.go │ │ ├── boil_table_names.go │ │ ├── boil_types.go │ │ ├── patient.go │ │ ├── patient_note.go │ │ ├── patient_note_test.go │ │ ├── patient_test.go │ │ ├── psql_main_test.go │ │ ├── psql_suites_test.go │ │ ├── psql_upsert.go │ │ ├── schema_migrations.go │ │ ├── schema_migrations_test.go │ │ ├── user.go │ │ └── user_test.go │ └── sqlboiler.toml ├── session │ ├── handler.go │ └── models.go └── user │ ├── dao.go │ ├── handler.go │ ├── models.go │ └── user_test.go ├── db-schema ├── migrate.sh └── migrations │ ├── 1_create_schema.down.sql │ ├── 1_create_schema.up.sql │ ├── 99_seed_data.down.sql │ └── 99_seed_data.up.sql ├── docker-compose.yml └── frontend ├── .gitignore ├── Dockerfile ├── README.md ├── cypress.json ├── cypress ├── fixtures │ └── example.json ├── integration │ └── spec.js ├── plugins │ └── index.js └── support │ ├── commands.js │ └── index.js ├── nginx.conf ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── client.js ├── components │ ├── HealthNoteEdit.svelte │ ├── HealthNoteView.svelte │ ├── Loading.svelte │ ├── Login.svelte │ ├── Nav.svelte │ └── Patient.svelte ├── routes │ ├── _error.svelte │ ├── _layout.svelte │ ├── about.svelte │ ├── admin.svelte │ ├── index.svelte │ ├── logout.svelte │ ├── patient.svelte │ ├── patients.svelte │ └── profile.svelte ├── server.js ├── service-worker.js ├── session.js └── template.html └── static ├── favicon.png ├── global.css ├── manifest.json ├── mock-health-notes.json ├── mock-patient.json ├── mock-patients.json └── mock-profile.json /README.md: -------------------------------------------------------------------------------- 1 | # svelte-golang-demo 2 | 3 | A demo app using Svelte as frontend and GoLang as backend. This app provides an web-based GUI for nurses to manage patient's health notes. 4 | 5 | ## site map 6 | 7 | - home (login, logout, register) 8 | - patients list view (accessible by nurses & admin, can add/edit patient) 9 | - patient view (list of notes, nurses can add new notes and edit existing) 10 | - ** admin (add/edit users: admin & nurses) 11 | - ** profile (view/modify personal details for logged-in user) 12 | 13 | ** not implemented 14 | 15 | ## application design 16 | 17 | #### frontend 18 | 19 | Svelte is used to compile frontend into static html/css/js pages that can be served over HTTP(S). JWT is used to authenticate client (web browser), as well as validate client session using stateless servers. 20 | 21 | One advantage of using svelte is its extremely small and fast compiled size. Site built with svelt can fun very fast and require little resources, this can be advantageous especially in context of mobile devices. Browser compatibility of the target mobile device should be investigated. 22 | 23 | Internationalization possibiles will not be address; if needed, it should be possible to achieve localization using plugins such as [svelte-i18n](https://github.com/kaisermann/svelte-i18n). 24 | 25 | #### backend 26 | 27 | A single goLang backend service is used to authenticate and serve all api requests. The backend will be used to authenticate client session by providing JWT tokens. 28 | 29 | if/when the need arises to separate the backend into individual microserves, the backend folder structure is designed to make this process relatively simple. Some code refactoring may need to be done, for example, the session service may be updated to call user service endpoint instead of accessing it directly 30 | 31 | Data is persisted by backend using postgres sql using (SQLBoiler)[https://github.com/volatiletech/sqlboiler] 32 | 33 | #### services & data modelling 34 | 35 | also refer to [api/README.md](api/README.md) for sample request/response 36 | 37 | - auth service 38 | - /api/v1/session 39 | - POST/DELETE (login, logout) 40 | - /api/v1/regiser 41 | - POST (register new userz) 42 | 43 | - user service 44 | - /api/v1/users 45 | - GET 46 | - /api/v1/user?user-id= 47 | - POST/GET/DELETE (not implemented) 48 | 49 | - dao user 50 | - userid (login id) 51 | - name (full name) 52 | - secret (salted hashed password) 53 | - last-login (not implemented) 54 | - created-time 55 | - last-modified (not implemented) 56 | 57 | - patient service 58 | - /api/v1/patients 59 | - GET (list) 60 | - /api/v1/patient?patient-id= 61 | - POST/GET/DELETE (single) 62 | - /api/v1/patient-notes?patient-id= 63 | - GET (list) 64 | - /api/v1/patient-note?note-id= 65 | - POST (new health note) 66 | - GET/DELETE (not implemented) 67 | 68 | - dao patient 69 | - patientid (uuid) 70 | - name (full name) 71 | - location (facility-id, bed-id, room-id, etc) 72 | - created-time 73 | - last-modified (not implemented) 74 | - dao patient-note 75 | - noteid (uuid) 76 | - userid (fk) 77 | - patientid (fk) 78 | - note (text) 79 | - created-time 80 | - last-modified (not implemented) 81 | 82 | ## repository folder structure 83 | 84 | - project root 85 | - api (folder) - (not implemented) 86 | - api definition 87 | - swagger/openapi stub generation scripts 88 | - frontend (folder) 89 | - src code, svelte 90 | - Dockerfiles for dev & testing 91 | - backend (folder) 92 | - src code 93 | - Dockerfiles for dev & testing 94 | - db-schema (folder) 95 | - schema definition 96 | - pre-seeded data for dev/testing 97 | - db schema migration scripts (https://github.com/golang-migrate/migrate) 98 | - db orm generator (sqlboiler, https://github.com/lqs/sqlingo) 99 | - integration-tests (folder) (not implemented) 100 | - synthetic testing (eg. selenium) 101 | - Docker-compose files for dev & testing 102 | - cicd (folder) (not implemented) 103 | - Dockerfiles for ci/cd & production services 104 | - cloud deployment manifests (k8s, terraform, etc) 105 | - ci/cd bash scripts 106 | 107 | ## ci/cd considerations 108 | 109 | (not implemented) code lint, unit testing, gui testing in ci/cd. Dockerfiles provided to facilitate build, test, and service execution environments. 110 | 111 | ## sample deployment architecture 112 | 113 | frontend website can deployed as static website; CDN may be utilized. Backend services can be deployed as stateless containerized (K8S) or serverless cloud architecture, using L7 cloud load balancers with TLS termination. A reverse proxy (or load balancer with path-based-routing support) may be used to scale different APIs individually, even if the codebase for different APIs is not separated. 114 | 115 | ## service monitoring consideration 116 | 117 | frontend can be instrumented with services such as Google Analytics. backend goLang services can be instrumented for Prometheus using official [go client](https://github.com/prometheus/client_golang), and for OpenTracing using [OpenTracing API for Go](https://github.com/opentracing/opentracing-go) 118 | 119 | ## future work 120 | 121 | - check licensing 122 | - openapi/swagger definition & stub generation 123 | - frontend unit tests 124 | - frontend instrumentation (eg. Google Analytics) 125 | - backend unit tests 126 | - backend instrumentation (eg. prometheus, opentracing) 127 | - integration tests 128 | - code lint in ci/cd 129 | - look into more secure authentication services/products & practices 130 | 131 | ## build & run compiled pages locally 132 | 133 | to run frontend in developer mode, see [frontend/README.md](frontend/README.md) 134 | 135 | ```sh 136 | # (re)build images & start 137 | docker-compose up --build 138 | 139 | # surf http://localhost:5000/ 140 | 141 | # stop 142 | docker-compose down 143 | ``` -------------------------------------------------------------------------------- /api/README.md: -------------------------------------------------------------------------------- 1 | # api definitions 2 | 3 | list of api provided by backend service & sample data format 4 | 5 | - TODO: use openapi/swagger generation tools 6 | ``` 7 | - auth service 8 | - /api/v1/session 9 | - POST(login) 10 | request: 11 | { 12 | "username": "marco", 13 | "password": "" 14 | } 15 | response: 16 | { 17 | "name": "Marco Polo", 18 | "username": "marco", 19 | "roles": ["nurse","admin"] 20 | } 21 | /DELETE (logout) 22 | - /api/v1/regiser 23 | - POST (register new user) 24 | request: 25 | { 26 | "name": "Marco Polo", 27 | "username": "marco", 28 | "password": "", 29 | "roles": ["nurse","admin"] 30 | } 31 | response: 32 | { 33 | "username": "marco", 34 | "roles": ["nurse","admin"] 35 | } 36 | 37 | - user service 38 | - /api/v1/users (not implemented) 39 | - GET 40 | response: 41 | [{ 42 | "name": "Marco Polo", 43 | "username": "marco", 44 | "roles": ["nurse","admin"] 45 | },{ 46 | "name": "Nancy Kim", 47 | "username": "nancy", 48 | "roles": ["nurse"] 49 | }] 50 | - /api/v1/user?user-id= 51 | - POST/GET/DELETE (not implemented) 52 | 53 | - patient service 54 | - /api/v1/patients 55 | - GET (list) 56 | response: 57 | [{ 58 | "name": "Patrick", 59 | "patientid": "patient1", 60 | "created" : "2020-08-02T11:15:08.259739Z" 61 | },{ 62 | "name": "Nancy Kim", 63 | "patientid": "patient2", 64 | "created" : "2020-08-02T11:15:08.259739Z" 65 | }] 66 | - /api/v1/patient?patient-id= 67 | - GET 68 | response: 69 | { 70 | "name": "Patrick", 71 | "patientid": "patient1", 72 | "created" : "2020-08-02T11:15:08.259739Z" 73 | } 74 | - /api/v1/patient-notes?patient-id= 75 | - GET (list) 76 | response: 77 | [{ 78 | "noteid":"6c7c14ee-d4b1-11ea-9733-0242ac130004", 79 | "patientid":"patient1", 80 | "userid":"nurse1", 81 | "note":"slight fever 38C", 82 | "created":"2020-08-02T11:15:08.259739Z" 83 | }, 84 | { 85 | "noteid":"6e9c4cd3-d4b1-11ea-9733-0242ac130004", 86 | "patientid":"patient1", 87 | "userid":"nurse1", 88 | "note":"looks good ot me", 89 | "created":"2020-08-02T11:15:11.826367Z" 90 | }] 91 | - /api/v1/patient-note?note-id= 92 | - POST (new health note) 93 | request: 94 | { 95 | "patientid":"patient1", 96 | "userid":"nurse1", 97 | "note":"efaef" 98 | } 99 | response: 100 | { 101 | "noteid":"6e9c4cd3-d4b1-11ea-9733-0242ac130004", 102 | "patientid":"patient1", 103 | "userid":"nurse1", 104 | "note":"efaef", 105 | "created":"2020-08-02T11:15:11.826367Z" 106 | } 107 | ``` -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | backend -------------------------------------------------------------------------------- /backend/Dockerfile: -------------------------------------------------------------------------------- 1 | # builds app and container to serve the backend 2 | FROM golang:alpine3.12 as builder 3 | RUN mkdir /build 4 | ADD . /build/ 5 | WORKDIR /build 6 | # TODO: find a workaround for managing go deps to use fixed dep versions 7 | RUN go get 8 | # TODO: add go test ./... (need to use golang:stretch) 9 | RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o main . 10 | 11 | FROM scratch 12 | COPY --from=builder /build/main /app/ 13 | WORKDIR /app 14 | CMD ["./main","-addr", "0.0.0.0:8000"] 15 | -------------------------------------------------------------------------------- /backend/README.md: -------------------------------------------------------------------------------- 1 | # backend 2 | 3 | ```sh 4 | # run 5 | go get 6 | go build 7 | ./backend 8 | 9 | # test 10 | brew install postgres # mac, only needed for running orm tests 11 | go test ./... # tests all sub packages, incliding orm tests (./patientdb) 12 | 13 | ## for go generate (generate sql boiler orm) 14 | # install sqlboiler w/postgresql locally 15 | GO111MODULE=off go get -u -t github.com/volatiletech/sqlboiler 16 | GO111MODULE=off go get github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql 17 | # if that doesnt work, try 18 | go get -u -t -v github.com/volatiletech/sqlboiler 19 | go get -u -v github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql 20 | # (optional) install tools for sql boiler test 21 | brew install libpq 22 | # update db orm from schema 23 | (cd ./patientdb && go generate) 24 | 25 | ## running backend in docker container 26 | # build app as docker image 27 | docker build . -t patient-backend 28 | 29 | # run app as docker image 30 | docker run --rm -it -p 8000:8000 patient-backend 31 | ``` 32 | 33 | # todo 34 | - health checks that tests db connection 35 | - unit tests 36 | -------------------------------------------------------------------------------- /backend/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/davidk81/svelte-golang-demo/backend 2 | 3 | require ( 4 | github.com/DATA-DOG/go-sqlmock v1.4.1 // indirect 5 | github.com/dgrijalva/jwt-go v3.2.0+incompatible 6 | github.com/friendsofgo/errors v0.9.2 7 | github.com/fsnotify/fsnotify v1.4.9 // indirect 8 | github.com/gofrs/uuid v3.2.0+incompatible // indirect 9 | github.com/google/uuid v1.1.1 10 | github.com/kat-co/vala v0.0.0-20170210184112-42e1d8b61f12 11 | github.com/lib/pq v1.8.0 12 | github.com/mitchellh/mapstructure v1.3.3 // indirect 13 | github.com/pelletier/go-toml v1.8.0 // indirect 14 | github.com/spf13/afero v1.3.2 // indirect 15 | github.com/spf13/cast v1.3.1 // indirect 16 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 17 | github.com/spf13/pflag v1.0.5 // indirect 18 | github.com/spf13/viper v1.7.1 19 | github.com/valyala/fasthttp v1.15.1 20 | github.com/volatiletech/inflect v0.0.1 // indirect 21 | github.com/volatiletech/null v8.0.0+incompatible 22 | github.com/volatiletech/sqlboiler v3.7.1+incompatible 23 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 24 | golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1 // indirect 25 | golang.org/x/text v0.3.3 // indirect 26 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect 27 | gopkg.in/ini.v1 v1.57.0 // indirect 28 | ) 29 | 30 | go 1.13 31 | -------------------------------------------------------------------------------- /backend/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // backend http server for patient service demo 4 | // usage: ./backend --help 5 | 6 | import ( 7 | "flag" 8 | "fmt" 9 | "log" 10 | "os" 11 | "os/signal" 12 | "syscall" 13 | 14 | "github.com/davidk81/svelte-golang-demo/backend/patient" 15 | "github.com/davidk81/svelte-golang-demo/backend/patientdb" 16 | "github.com/davidk81/svelte-golang-demo/backend/session" 17 | _ "github.com/lib/pq" 18 | "github.com/valyala/fasthttp" 19 | ) 20 | 21 | // config flags 22 | var ( 23 | dbConn = flag.String("db", "host=localhost dbname=patientdb user=docker password=docker sslmode=disable", "db connection string") 24 | addr = flag.String("addr", "localhost:8000", "tcp listen address & port") 25 | compress = flag.Bool("compress", false, "response compression [true/false]") 26 | ) 27 | 28 | func main() { 29 | flag.Parse() 30 | 31 | // init db connection 32 | patientdb.Init(*dbConn) 33 | 34 | h := requestHandler 35 | if *compress { 36 | h = fasthttp.CompressHandler(h) 37 | } 38 | 39 | // start server 40 | go func() { 41 | log.Println("server starting on port", *addr) 42 | if err := fasthttp.ListenAndServe(*addr, h); err != nil { 43 | log.Fatalf("error during ListenAndServe: %s", err) 44 | } 45 | }() 46 | 47 | // wait for cleanup signal 48 | sigs := make(chan os.Signal, 1) 49 | done := make(chan bool, 1) 50 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 51 | go func() { 52 | _ = <-sigs 53 | done <- true 54 | }() 55 | <-done 56 | 57 | // cleanup 58 | patientdb.Close() 59 | } 60 | 61 | func route(ctx *fasthttp.RequestCtx) error { 62 | // routes that dont need session 63 | // TODO: enable session checking 64 | apiPrefix := "/api/v1/" 65 | switch string(ctx.Path()) { 66 | case apiPrefix + "session": 67 | return session.HandleSession(ctx) 68 | case apiPrefix + "register": 69 | return session.HandleRegister(ctx) 70 | case "/healthz": 71 | return handleHealth(ctx) 72 | } 73 | 74 | // check users' session token 75 | _, err := session.ValidateSession(ctx, "nurse") 76 | if err != nil { 77 | ctx.Response.SetStatusCode(fasthttp.StatusUnauthorized) 78 | return nil 79 | } 80 | 81 | // routes that need session 82 | switch string(ctx.Path()) { 83 | case apiPrefix + "patients": 84 | return patient.HandlePatientList(ctx) 85 | case apiPrefix + "patient": 86 | return patient.HandlePatient(ctx) 87 | case apiPrefix + "patient/note": 88 | return patient.HandlePatientNote(ctx) 89 | case apiPrefix + "patient/notes": 90 | return patient.HandlePatientNoteList(ctx) 91 | default: 92 | ctx.NotFound() 93 | return nil 94 | } 95 | } 96 | 97 | func requestHandler(ctx *fasthttp.RequestCtx) { 98 | log.Printf("%s %s\n", ctx.Request.Header.Method(), ctx.URI().RequestURI()) 99 | 100 | // enable cors for development 101 | ctx.Response.Header.Set("access-control-allow-credentials", "true") 102 | ctx.Response.Header.Set("access-control-allow-origin", string(ctx.Request.Header.Peek("Origin"))) 103 | ctx.Response.Header.Set("access-control-expose-headers", "WWW-Authenticate,Server-Authorization") 104 | ctx.Response.Header.Set("cache-control", "no-cache") 105 | ctx.Response.Header.Set("Connection", "keep-alive") 106 | 107 | if ctx.IsOptions() { 108 | handleMethodOptions(ctx) 109 | return 110 | } 111 | 112 | // handle request routes 113 | err := route(ctx) 114 | if err != nil { 115 | fmt.Println(err) 116 | ctx.SetBody([]byte(err.Error())) 117 | ctx.SetStatusCode(fasthttp.StatusInternalServerError) 118 | return 119 | } 120 | } 121 | 122 | func handleMethodOptions(ctx *fasthttp.RequestCtx) { 123 | ctx.Response.Header.Set("access-control-allow-headers", "Accept,Authorization,Content-Type,If-None-Match") 124 | ctx.Response.Header.Set("access-control-allow-methods", string(ctx.Request.Header.Peek("Access-Control-Request-Method"))) 125 | ctx.Response.Header.Set("access-control-max-age", "86400") 126 | ctx.SetStatusCode(fasthttp.StatusOK) 127 | } 128 | 129 | func handleHealth(ctx *fasthttp.RequestCtx) error { 130 | ctx.SetStatusCode(fasthttp.StatusOK) 131 | return nil 132 | } 133 | -------------------------------------------------------------------------------- /backend/patient/dao.go: -------------------------------------------------------------------------------- 1 | package patient 2 | 3 | // handles database operations for patient and patient_notes tables 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/davidk81/svelte-golang-demo/backend/patientdb" 9 | "github.com/davidk81/svelte-golang-demo/backend/patientdb/models" 10 | "github.com/google/uuid" 11 | "github.com/volatiletech/sqlboiler/boil" 12 | "github.com/volatiletech/sqlboiler/queries/qm" 13 | ) 14 | 15 | // getPatients (list) 16 | func getPatients(ctx context.Context) (models.PatientSlice, error) { 17 | return models.Patients(qm.Limit(20)).All(ctx, patientdb.DB()) 18 | } 19 | 20 | // getPatient returns patient with matching patientID 21 | func getPatient(ctx context.Context, patientID string) (*models.Patient, error) { 22 | return models.Patients(models.PatientWhere.Patientid.EQ(patientID)).One(ctx, patientdb.DB()) 23 | } 24 | 25 | // getPatientNotes (list) 26 | func getPatientNotes(ctx context.Context, patientID string) (models.PatientNoteSlice, error) { 27 | // TODO: add orderby clause 28 | return models.PatientNotes(models.PatientNoteWhere.Patient_Id.EQ(patientID)).All(ctx, patientdb.DB()) 29 | } 30 | 31 | // addPatientNote adds a new note to the patient 32 | func addPatientNote(ctx context.Context, note *models.PatientNote) error { 33 | id, err := uuid.NewUUID() 34 | if err != nil { 35 | return err 36 | } 37 | note.Noteid = id.String() 38 | return (*note).Insert(ctx, patientdb.DB(), boil.Infer()) 39 | } 40 | -------------------------------------------------------------------------------- /backend/patient/handlerNotes.go: -------------------------------------------------------------------------------- 1 | package patient 2 | 3 | // handles http requests for /patient/note and /patient/notes 4 | 5 | import ( 6 | "encoding/json" 7 | 8 | "github.com/davidk81/svelte-golang-demo/backend/patientdb/models" 9 | "github.com/valyala/fasthttp" 10 | ) 11 | 12 | // HandlePatientNote entrypoint http request handler /patient/note 13 | func HandlePatientNote(ctx *fasthttp.RequestCtx) error { 14 | switch string(ctx.Request.Header.Method()) { 15 | case "POST": 16 | return handleMethodNotePost(ctx) 17 | default: 18 | ctx.NotFound() 19 | return nil 20 | } 21 | } 22 | 23 | // HandlePatientNoteList entrypoint http request handler /patient/notes 24 | func HandlePatientNoteList(ctx *fasthttp.RequestCtx) error { 25 | switch string(ctx.Request.Header.Method()) { 26 | case "GET": 27 | return handleMethodNoteGetList(ctx) 28 | default: 29 | ctx.NotFound() 30 | return nil 31 | } 32 | } 33 | 34 | func handleMethodNotePost(ctx *fasthttp.RequestCtx) error { 35 | // decode post body 36 | var note models.PatientNote 37 | err := json.Unmarshal(ctx.Request.Body(), ¬e) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | err = addPatientNote(ctx, ¬e) 43 | if err != nil { 44 | return err 45 | } 46 | 47 | // return patient info in response 48 | b, err := json.Marshal(note) 49 | if err != nil { 50 | return err 51 | } 52 | ctx.SetBody([]byte(b)) 53 | ctx.SetStatusCode(fasthttp.StatusCreated) 54 | return nil 55 | } 56 | 57 | func handleMethodNoteGetList(ctx *fasthttp.RequestCtx) error { 58 | patientID := string(ctx.QueryArgs().Peek("patientid")) 59 | 60 | // return patient info in response 61 | p, err := getPatientNotes(ctx, patientID) 62 | if err != nil { 63 | return err 64 | } 65 | b, err := json.Marshal(p) 66 | if err != nil { 67 | return err 68 | } 69 | ctx.SetBody([]byte(b)) 70 | ctx.SetStatusCode(fasthttp.StatusOK) 71 | return nil 72 | } 73 | -------------------------------------------------------------------------------- /backend/patient/handlerPatients.go: -------------------------------------------------------------------------------- 1 | package patient 2 | 3 | // handles http requests for /patient and /patients 4 | 5 | import ( 6 | "encoding/json" 7 | 8 | "github.com/davidk81/svelte-golang-demo/backend/patientdb/models" 9 | "github.com/valyala/fasthttp" 10 | ) 11 | 12 | // HandlePatient entrypoint http request handler for /patient 13 | func HandlePatient(ctx *fasthttp.RequestCtx) error { 14 | switch string(ctx.Request.Header.Method()) { 15 | case "GET": 16 | return handleMethodGet(ctx) 17 | case "POST": 18 | return handleMethodPost(ctx) // not implemented 19 | case "DELETE": 20 | return handleMethodDelete(ctx) // not implemented 21 | default: 22 | ctx.NotFound() 23 | return nil 24 | } 25 | } 26 | 27 | // HandlePatientList entrypoint http request handler for /patients 28 | func HandlePatientList(ctx *fasthttp.RequestCtx) error { 29 | switch string(ctx.Request.Header.Method()) { 30 | case "GET": 31 | return handleMethodGetList(ctx) 32 | default: 33 | ctx.NotFound() 34 | return nil 35 | } 36 | } 37 | 38 | func handleMethodDelete(ctx *fasthttp.RequestCtx) error { 39 | // TODO 40 | ctx.SetStatusCode(fasthttp.StatusNotImplemented) 41 | return nil 42 | } 43 | 44 | func handleMethodPost(ctx *fasthttp.RequestCtx) error { 45 | // decode post body 46 | var patient models.Patient 47 | err := json.Unmarshal(ctx.Request.Body(), &patient) 48 | if err != nil { 49 | ctx.SetStatusCode(fasthttp.StatusBadRequest) 50 | return nil 51 | } 52 | 53 | // TODO: validate data 54 | // TODO: insert to db 55 | 56 | // return patient info in response 57 | b, err := json.Marshal(patient) 58 | if err != nil { 59 | return err 60 | } 61 | ctx.SetBody([]byte(b)) 62 | // ctx.SetStatusCode(fasthttp.StatusCreated) 63 | ctx.SetStatusCode(fasthttp.StatusNotImplemented) 64 | return nil 65 | } 66 | 67 | func handleMethodGetList(ctx *fasthttp.RequestCtx) error { 68 | p, err := getPatients(ctx) 69 | if err != nil { 70 | return err 71 | } 72 | b, err := json.Marshal(p) 73 | if err != nil { 74 | return err 75 | } 76 | ctx.SetBody([]byte(b)) 77 | ctx.SetStatusCode(fasthttp.StatusOK) 78 | return nil 79 | } 80 | 81 | func handleMethodGet(ctx *fasthttp.RequestCtx) error { 82 | patientID := string(ctx.QueryArgs().Peek("patientid")) 83 | 84 | // return patient info in response 85 | p, err := getPatient(ctx, patientID) 86 | if err != nil { 87 | return err 88 | } 89 | b, err := json.Marshal(p) 90 | if err != nil { 91 | return err 92 | } 93 | ctx.SetBody([]byte(b)) 94 | ctx.SetStatusCode(fasthttp.StatusOK) 95 | return nil 96 | } 97 | -------------------------------------------------------------------------------- /backend/patientdb/init.go: -------------------------------------------------------------------------------- 1 | package patientdb 2 | 3 | // line below enables 'go generate' cmd to update orm stubs 4 | //go:generate sqlboiler --wipe psql 5 | 6 | import ( 7 | "database/sql" 8 | ) 9 | 10 | var sharedb *sql.DB 11 | 12 | // Init initialize database connection 13 | func Init(dbConn string) { 14 | db, err := sql.Open("postgres", dbConn) 15 | if err != nil { 16 | panic(err) 17 | } 18 | sharedb = db 19 | 20 | // TODO: look into setting exec db context globally instead 21 | // boil.SetDB(db) 22 | // boil.DebugMode = true 23 | } 24 | 25 | // DB returns instance for patient db 26 | func DB() *sql.DB { 27 | return sharedb 28 | } 29 | 30 | func Close() { 31 | sharedb.Close() 32 | } 33 | -------------------------------------------------------------------------------- /backend/patientdb/models/boil_main_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 3.7.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package models 5 | 6 | import ( 7 | "database/sql" 8 | "flag" 9 | "fmt" 10 | "math/rand" 11 | "os" 12 | "path/filepath" 13 | "strings" 14 | "testing" 15 | "time" 16 | 17 | "github.com/spf13/viper" 18 | "github.com/volatiletech/sqlboiler/boil" 19 | ) 20 | 21 | var flagDebugMode = flag.Bool("test.sqldebug", false, "Turns on debug mode for SQL statements") 22 | var flagConfigFile = flag.String("test.config", "", "Overrides the default config") 23 | 24 | const outputDirDepth = 1 25 | 26 | var ( 27 | dbMain tester 28 | ) 29 | 30 | type tester interface { 31 | setup() error 32 | conn() (*sql.DB, error) 33 | teardown() error 34 | } 35 | 36 | func TestMain(m *testing.M) { 37 | if dbMain == nil { 38 | fmt.Println("no dbMain tester interface was ready") 39 | os.Exit(-1) 40 | } 41 | 42 | rand.Seed(time.Now().UnixNano()) 43 | 44 | flag.Parse() 45 | 46 | var err error 47 | 48 | // Load configuration 49 | err = initViper() 50 | if err != nil { 51 | fmt.Println("unable to load config file") 52 | os.Exit(-2) 53 | } 54 | 55 | // Set DebugMode so we can see generated sql statements 56 | boil.DebugMode = *flagDebugMode 57 | 58 | if err = dbMain.setup(); err != nil { 59 | fmt.Println("Unable to execute setup:", err) 60 | os.Exit(-4) 61 | } 62 | 63 | conn, err := dbMain.conn() 64 | if err != nil { 65 | fmt.Println("failed to get connection:", err) 66 | } 67 | 68 | var code int 69 | boil.SetDB(conn) 70 | code = m.Run() 71 | 72 | if err = dbMain.teardown(); err != nil { 73 | fmt.Println("Unable to execute teardown:", err) 74 | os.Exit(-5) 75 | } 76 | 77 | os.Exit(code) 78 | } 79 | 80 | func initViper() error { 81 | if flagConfigFile != nil && *flagConfigFile != "" { 82 | viper.SetConfigFile(*flagConfigFile) 83 | if err := viper.ReadInConfig(); err != nil { 84 | return err 85 | } 86 | return nil 87 | } 88 | 89 | var err error 90 | 91 | viper.SetConfigName("sqlboiler") 92 | 93 | configHome := os.Getenv("XDG_CONFIG_HOME") 94 | homePath := os.Getenv("HOME") 95 | wd, err := os.Getwd() 96 | if err != nil { 97 | wd = strings.Repeat("../", outputDirDepth) 98 | } else { 99 | wd = wd + strings.Repeat("/..", outputDirDepth) 100 | } 101 | 102 | configPaths := []string{wd} 103 | if len(configHome) > 0 { 104 | configPaths = append(configPaths, filepath.Join(configHome, "sqlboiler")) 105 | } else { 106 | configPaths = append(configPaths, filepath.Join(homePath, ".config/sqlboiler")) 107 | } 108 | 109 | for _, p := range configPaths { 110 | viper.AddConfigPath(p) 111 | } 112 | 113 | // Ignore errors here, fall back to defaults and validation to provide errs 114 | _ = viper.ReadInConfig() 115 | viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) 116 | viper.AutomaticEnv() 117 | 118 | return nil 119 | } 120 | -------------------------------------------------------------------------------- /backend/patientdb/models/boil_queries.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 3.7.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package models 5 | 6 | import ( 7 | "github.com/volatiletech/sqlboiler/drivers" 8 | "github.com/volatiletech/sqlboiler/queries" 9 | "github.com/volatiletech/sqlboiler/queries/qm" 10 | ) 11 | 12 | var dialect = drivers.Dialect{ 13 | LQ: 0x22, 14 | RQ: 0x22, 15 | 16 | UseIndexPlaceholders: true, 17 | UseLastInsertID: false, 18 | UseSchema: false, 19 | UseDefaultKeyword: true, 20 | UseAutoColumns: false, 21 | UseTopClause: false, 22 | UseOutputClause: false, 23 | UseCaseWhenExistsClause: false, 24 | } 25 | 26 | // NewQuery initializes a new Query using the passed in QueryMods 27 | func NewQuery(mods ...qm.QueryMod) *queries.Query { 28 | q := &queries.Query{} 29 | queries.SetDialect(q, &dialect) 30 | qm.Apply(q, mods...) 31 | 32 | return q 33 | } 34 | -------------------------------------------------------------------------------- /backend/patientdb/models/boil_queries_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 3.7.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package models 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | "io" 10 | "io/ioutil" 11 | "math/rand" 12 | "regexp" 13 | 14 | "github.com/volatiletech/sqlboiler/boil" 15 | ) 16 | 17 | var dbNameRand *rand.Rand 18 | 19 | func MustTx(transactor boil.ContextTransactor, err error) boil.ContextTransactor { 20 | if err != nil { 21 | panic(fmt.Sprintf("Cannot create a transactor: %s", err)) 22 | } 23 | return transactor 24 | } 25 | 26 | func newFKeyDestroyer(regex *regexp.Regexp, reader io.Reader) io.Reader { 27 | return &fKeyDestroyer{ 28 | reader: reader, 29 | rgx: regex, 30 | } 31 | } 32 | 33 | type fKeyDestroyer struct { 34 | reader io.Reader 35 | buf *bytes.Buffer 36 | rgx *regexp.Regexp 37 | } 38 | 39 | func (f *fKeyDestroyer) Read(b []byte) (int, error) { 40 | if f.buf == nil { 41 | all, err := ioutil.ReadAll(f.reader) 42 | if err != nil { 43 | return 0, err 44 | } 45 | 46 | all = bytes.Replace(all, []byte{'\r', '\n'}, []byte{'\n'}, -1) 47 | all = f.rgx.ReplaceAll(all, []byte{}) 48 | f.buf = bytes.NewBuffer(all) 49 | } 50 | 51 | return f.buf.Read(b) 52 | } 53 | -------------------------------------------------------------------------------- /backend/patientdb/models/boil_suites_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 3.7.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package models 5 | 6 | import "testing" 7 | 8 | // This test suite runs each operation test in parallel. 9 | // Example, if your database has 3 tables, the suite will run: 10 | // table1, table2 and table3 Delete in parallel 11 | // table1, table2 and table3 Insert in parallel, and so forth. 12 | // It does NOT run each operation group in parallel. 13 | // Separating the tests thusly grants avoidance of Postgres deadlocks. 14 | func TestParent(t *testing.T) { 15 | t.Run("Patients", testPatients) 16 | t.Run("PatientNotes", testPatientNotes) 17 | t.Run("SchemaMigrations", testSchemaMigrations) 18 | t.Run("Users", testUsers) 19 | } 20 | 21 | func TestDelete(t *testing.T) { 22 | t.Run("Patients", testPatientsDelete) 23 | t.Run("PatientNotes", testPatientNotesDelete) 24 | t.Run("SchemaMigrations", testSchemaMigrationsDelete) 25 | t.Run("Users", testUsersDelete) 26 | } 27 | 28 | func TestQueryDeleteAll(t *testing.T) { 29 | t.Run("Patients", testPatientsQueryDeleteAll) 30 | t.Run("PatientNotes", testPatientNotesQueryDeleteAll) 31 | t.Run("SchemaMigrations", testSchemaMigrationsQueryDeleteAll) 32 | t.Run("Users", testUsersQueryDeleteAll) 33 | } 34 | 35 | func TestSliceDeleteAll(t *testing.T) { 36 | t.Run("Patients", testPatientsSliceDeleteAll) 37 | t.Run("PatientNotes", testPatientNotesSliceDeleteAll) 38 | t.Run("SchemaMigrations", testSchemaMigrationsSliceDeleteAll) 39 | t.Run("Users", testUsersSliceDeleteAll) 40 | } 41 | 42 | func TestExists(t *testing.T) { 43 | t.Run("Patients", testPatientsExists) 44 | t.Run("PatientNotes", testPatientNotesExists) 45 | t.Run("SchemaMigrations", testSchemaMigrationsExists) 46 | t.Run("Users", testUsersExists) 47 | } 48 | 49 | func TestFind(t *testing.T) { 50 | t.Run("Patients", testPatientsFind) 51 | t.Run("PatientNotes", testPatientNotesFind) 52 | t.Run("SchemaMigrations", testSchemaMigrationsFind) 53 | t.Run("Users", testUsersFind) 54 | } 55 | 56 | func TestBind(t *testing.T) { 57 | t.Run("Patients", testPatientsBind) 58 | t.Run("PatientNotes", testPatientNotesBind) 59 | t.Run("SchemaMigrations", testSchemaMigrationsBind) 60 | t.Run("Users", testUsersBind) 61 | } 62 | 63 | func TestOne(t *testing.T) { 64 | t.Run("Patients", testPatientsOne) 65 | t.Run("PatientNotes", testPatientNotesOne) 66 | t.Run("SchemaMigrations", testSchemaMigrationsOne) 67 | t.Run("Users", testUsersOne) 68 | } 69 | 70 | func TestAll(t *testing.T) { 71 | t.Run("Patients", testPatientsAll) 72 | t.Run("PatientNotes", testPatientNotesAll) 73 | t.Run("SchemaMigrations", testSchemaMigrationsAll) 74 | t.Run("Users", testUsersAll) 75 | } 76 | 77 | func TestCount(t *testing.T) { 78 | t.Run("Patients", testPatientsCount) 79 | t.Run("PatientNotes", testPatientNotesCount) 80 | t.Run("SchemaMigrations", testSchemaMigrationsCount) 81 | t.Run("Users", testUsersCount) 82 | } 83 | 84 | func TestHooks(t *testing.T) { 85 | t.Run("Patients", testPatientsHooks) 86 | t.Run("PatientNotes", testPatientNotesHooks) 87 | t.Run("SchemaMigrations", testSchemaMigrationsHooks) 88 | t.Run("Users", testUsersHooks) 89 | } 90 | 91 | func TestInsert(t *testing.T) { 92 | t.Run("Patients", testPatientsInsert) 93 | t.Run("Patients", testPatientsInsertWhitelist) 94 | t.Run("PatientNotes", testPatientNotesInsert) 95 | t.Run("PatientNotes", testPatientNotesInsertWhitelist) 96 | t.Run("SchemaMigrations", testSchemaMigrationsInsert) 97 | t.Run("SchemaMigrations", testSchemaMigrationsInsertWhitelist) 98 | t.Run("Users", testUsersInsert) 99 | t.Run("Users", testUsersInsertWhitelist) 100 | } 101 | 102 | // TestToOne tests cannot be run in parallel 103 | // or deadlocks can occur. 104 | func TestToOne(t *testing.T) { 105 | t.Run("PatientNoteToPatientUsingPatientid", testPatientNoteToOnePatientUsingPatientid) 106 | t.Run("PatientNoteToUserUsingUserid", testPatientNoteToOneUserUsingUserid) 107 | } 108 | 109 | // TestOneToOne tests cannot be run in parallel 110 | // or deadlocks can occur. 111 | func TestOneToOne(t *testing.T) {} 112 | 113 | // TestToMany tests cannot be run in parallel 114 | // or deadlocks can occur. 115 | func TestToMany(t *testing.T) { 116 | t.Run("PatientToPatientidPatientNotes", testPatientToManyPatientidPatientNotes) 117 | t.Run("UserToUseridPatientNotes", testUserToManyUseridPatientNotes) 118 | } 119 | 120 | // TestToOneSet tests cannot be run in parallel 121 | // or deadlocks can occur. 122 | func TestToOneSet(t *testing.T) { 123 | t.Run("PatientNoteToPatientUsingPatientidPatientNotes", testPatientNoteToOneSetOpPatientUsingPatientid) 124 | t.Run("PatientNoteToUserUsingUseridPatientNotes", testPatientNoteToOneSetOpUserUsingUserid) 125 | } 126 | 127 | // TestToOneRemove tests cannot be run in parallel 128 | // or deadlocks can occur. 129 | func TestToOneRemove(t *testing.T) {} 130 | 131 | // TestOneToOneSet tests cannot be run in parallel 132 | // or deadlocks can occur. 133 | func TestOneToOneSet(t *testing.T) {} 134 | 135 | // TestOneToOneRemove tests cannot be run in parallel 136 | // or deadlocks can occur. 137 | func TestOneToOneRemove(t *testing.T) {} 138 | 139 | // TestToManyAdd tests cannot be run in parallel 140 | // or deadlocks can occur. 141 | func TestToManyAdd(t *testing.T) { 142 | t.Run("PatientToPatientidPatientNotes", testPatientToManyAddOpPatientidPatientNotes) 143 | t.Run("UserToUseridPatientNotes", testUserToManyAddOpUseridPatientNotes) 144 | } 145 | 146 | // TestToManySet tests cannot be run in parallel 147 | // or deadlocks can occur. 148 | func TestToManySet(t *testing.T) {} 149 | 150 | // TestToManyRemove tests cannot be run in parallel 151 | // or deadlocks can occur. 152 | func TestToManyRemove(t *testing.T) {} 153 | 154 | func TestReload(t *testing.T) { 155 | t.Run("Patients", testPatientsReload) 156 | t.Run("PatientNotes", testPatientNotesReload) 157 | t.Run("SchemaMigrations", testSchemaMigrationsReload) 158 | t.Run("Users", testUsersReload) 159 | } 160 | 161 | func TestReloadAll(t *testing.T) { 162 | t.Run("Patients", testPatientsReloadAll) 163 | t.Run("PatientNotes", testPatientNotesReloadAll) 164 | t.Run("SchemaMigrations", testSchemaMigrationsReloadAll) 165 | t.Run("Users", testUsersReloadAll) 166 | } 167 | 168 | func TestSelect(t *testing.T) { 169 | t.Run("Patients", testPatientsSelect) 170 | t.Run("PatientNotes", testPatientNotesSelect) 171 | t.Run("SchemaMigrations", testSchemaMigrationsSelect) 172 | t.Run("Users", testUsersSelect) 173 | } 174 | 175 | func TestUpdate(t *testing.T) { 176 | t.Run("Patients", testPatientsUpdate) 177 | t.Run("PatientNotes", testPatientNotesUpdate) 178 | t.Run("SchemaMigrations", testSchemaMigrationsUpdate) 179 | t.Run("Users", testUsersUpdate) 180 | } 181 | 182 | func TestSliceUpdateAll(t *testing.T) { 183 | t.Run("Patients", testPatientsSliceUpdateAll) 184 | t.Run("PatientNotes", testPatientNotesSliceUpdateAll) 185 | t.Run("SchemaMigrations", testSchemaMigrationsSliceUpdateAll) 186 | t.Run("Users", testUsersSliceUpdateAll) 187 | } 188 | -------------------------------------------------------------------------------- /backend/patientdb/models/boil_table_names.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 3.7.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package models 5 | 6 | var TableNames = struct { 7 | Patient string 8 | PatientNote string 9 | SchemaMigrations string 10 | User string 11 | }{ 12 | Patient: "patient", 13 | PatientNote: "patient_note", 14 | SchemaMigrations: "schema_migrations", 15 | User: "user", 16 | } 17 | -------------------------------------------------------------------------------- /backend/patientdb/models/boil_types.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 3.7.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package models 5 | 6 | import ( 7 | "strconv" 8 | 9 | "github.com/friendsofgo/errors" 10 | "github.com/volatiletech/sqlboiler/boil" 11 | "github.com/volatiletech/sqlboiler/strmangle" 12 | ) 13 | 14 | // M type is for providing columns and column values to UpdateAll. 15 | type M map[string]interface{} 16 | 17 | // ErrSyncFail occurs during insert when the record could not be retrieved in 18 | // order to populate default value information. This usually happens when LastInsertId 19 | // fails or there was a primary key configuration that was not resolvable. 20 | var ErrSyncFail = errors.New("models: failed to synchronize data after insert") 21 | 22 | type insertCache struct { 23 | query string 24 | retQuery string 25 | valueMapping []uint64 26 | retMapping []uint64 27 | } 28 | 29 | type updateCache struct { 30 | query string 31 | valueMapping []uint64 32 | } 33 | 34 | func makeCacheKey(cols boil.Columns, nzDefaults []string) string { 35 | buf := strmangle.GetBuffer() 36 | 37 | buf.WriteString(strconv.Itoa(cols.Kind)) 38 | for _, w := range cols.Cols { 39 | buf.WriteString(w) 40 | } 41 | 42 | if len(nzDefaults) != 0 { 43 | buf.WriteByte('.') 44 | } 45 | for _, nz := range nzDefaults { 46 | buf.WriteString(nz) 47 | } 48 | 49 | str := buf.String() 50 | strmangle.PutBuffer(buf) 51 | return str 52 | } 53 | -------------------------------------------------------------------------------- /backend/patientdb/models/patient_note_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 3.7.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package models 5 | 6 | import ( 7 | "bytes" 8 | "context" 9 | "reflect" 10 | "testing" 11 | 12 | "github.com/volatiletech/sqlboiler/boil" 13 | "github.com/volatiletech/sqlboiler/queries" 14 | "github.com/volatiletech/sqlboiler/randomize" 15 | "github.com/volatiletech/sqlboiler/strmangle" 16 | ) 17 | 18 | var ( 19 | // Relationships sometimes use the reflection helper queries.Equal/queries.Assign 20 | // so force a package dependency in case they don't. 21 | _ = queries.Equal 22 | ) 23 | 24 | func testPatientNotes(t *testing.T) { 25 | t.Parallel() 26 | 27 | query := PatientNotes() 28 | 29 | if query.Query == nil { 30 | t.Error("expected a query, got nothing") 31 | } 32 | } 33 | 34 | func testPatientNotesDelete(t *testing.T) { 35 | t.Parallel() 36 | 37 | seed := randomize.NewSeed() 38 | var err error 39 | o := &PatientNote{} 40 | if err = randomize.Struct(seed, o, patientNoteDBTypes, true, patientNoteColumnsWithDefault...); err != nil { 41 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 42 | } 43 | 44 | ctx := context.Background() 45 | tx := MustTx(boil.BeginTx(ctx, nil)) 46 | defer func() { _ = tx.Rollback() }() 47 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 48 | t.Error(err) 49 | } 50 | 51 | if rowsAff, err := o.Delete(ctx, tx); err != nil { 52 | t.Error(err) 53 | } else if rowsAff != 1 { 54 | t.Error("should only have deleted one row, but affected:", rowsAff) 55 | } 56 | 57 | count, err := PatientNotes().Count(ctx, tx) 58 | if err != nil { 59 | t.Error(err) 60 | } 61 | 62 | if count != 0 { 63 | t.Error("want zero records, got:", count) 64 | } 65 | } 66 | 67 | func testPatientNotesQueryDeleteAll(t *testing.T) { 68 | t.Parallel() 69 | 70 | seed := randomize.NewSeed() 71 | var err error 72 | o := &PatientNote{} 73 | if err = randomize.Struct(seed, o, patientNoteDBTypes, true, patientNoteColumnsWithDefault...); err != nil { 74 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 75 | } 76 | 77 | ctx := context.Background() 78 | tx := MustTx(boil.BeginTx(ctx, nil)) 79 | defer func() { _ = tx.Rollback() }() 80 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 81 | t.Error(err) 82 | } 83 | 84 | if rowsAff, err := PatientNotes().DeleteAll(ctx, tx); err != nil { 85 | t.Error(err) 86 | } else if rowsAff != 1 { 87 | t.Error("should only have deleted one row, but affected:", rowsAff) 88 | } 89 | 90 | count, err := PatientNotes().Count(ctx, tx) 91 | if err != nil { 92 | t.Error(err) 93 | } 94 | 95 | if count != 0 { 96 | t.Error("want zero records, got:", count) 97 | } 98 | } 99 | 100 | func testPatientNotesSliceDeleteAll(t *testing.T) { 101 | t.Parallel() 102 | 103 | seed := randomize.NewSeed() 104 | var err error 105 | o := &PatientNote{} 106 | if err = randomize.Struct(seed, o, patientNoteDBTypes, true, patientNoteColumnsWithDefault...); err != nil { 107 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 108 | } 109 | 110 | ctx := context.Background() 111 | tx := MustTx(boil.BeginTx(ctx, nil)) 112 | defer func() { _ = tx.Rollback() }() 113 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 114 | t.Error(err) 115 | } 116 | 117 | slice := PatientNoteSlice{o} 118 | 119 | if rowsAff, err := slice.DeleteAll(ctx, tx); err != nil { 120 | t.Error(err) 121 | } else if rowsAff != 1 { 122 | t.Error("should only have deleted one row, but affected:", rowsAff) 123 | } 124 | 125 | count, err := PatientNotes().Count(ctx, tx) 126 | if err != nil { 127 | t.Error(err) 128 | } 129 | 130 | if count != 0 { 131 | t.Error("want zero records, got:", count) 132 | } 133 | } 134 | 135 | func testPatientNotesExists(t *testing.T) { 136 | t.Parallel() 137 | 138 | seed := randomize.NewSeed() 139 | var err error 140 | o := &PatientNote{} 141 | if err = randomize.Struct(seed, o, patientNoteDBTypes, true, patientNoteColumnsWithDefault...); err != nil { 142 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 143 | } 144 | 145 | ctx := context.Background() 146 | tx := MustTx(boil.BeginTx(ctx, nil)) 147 | defer func() { _ = tx.Rollback() }() 148 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 149 | t.Error(err) 150 | } 151 | 152 | e, err := PatientNoteExists(ctx, tx, o.Noteid) 153 | if err != nil { 154 | t.Errorf("Unable to check if PatientNote exists: %s", err) 155 | } 156 | if !e { 157 | t.Errorf("Expected PatientNoteExists to return true, but got false.") 158 | } 159 | } 160 | 161 | func testPatientNotesFind(t *testing.T) { 162 | t.Parallel() 163 | 164 | seed := randomize.NewSeed() 165 | var err error 166 | o := &PatientNote{} 167 | if err = randomize.Struct(seed, o, patientNoteDBTypes, true, patientNoteColumnsWithDefault...); err != nil { 168 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 169 | } 170 | 171 | ctx := context.Background() 172 | tx := MustTx(boil.BeginTx(ctx, nil)) 173 | defer func() { _ = tx.Rollback() }() 174 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 175 | t.Error(err) 176 | } 177 | 178 | patientNoteFound, err := FindPatientNote(ctx, tx, o.Noteid) 179 | if err != nil { 180 | t.Error(err) 181 | } 182 | 183 | if patientNoteFound == nil { 184 | t.Error("want a record, got nil") 185 | } 186 | } 187 | 188 | func testPatientNotesBind(t *testing.T) { 189 | t.Parallel() 190 | 191 | seed := randomize.NewSeed() 192 | var err error 193 | o := &PatientNote{} 194 | if err = randomize.Struct(seed, o, patientNoteDBTypes, true, patientNoteColumnsWithDefault...); err != nil { 195 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 196 | } 197 | 198 | ctx := context.Background() 199 | tx := MustTx(boil.BeginTx(ctx, nil)) 200 | defer func() { _ = tx.Rollback() }() 201 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 202 | t.Error(err) 203 | } 204 | 205 | if err = PatientNotes().Bind(ctx, tx, o); err != nil { 206 | t.Error(err) 207 | } 208 | } 209 | 210 | func testPatientNotesOne(t *testing.T) { 211 | t.Parallel() 212 | 213 | seed := randomize.NewSeed() 214 | var err error 215 | o := &PatientNote{} 216 | if err = randomize.Struct(seed, o, patientNoteDBTypes, true, patientNoteColumnsWithDefault...); err != nil { 217 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 218 | } 219 | 220 | ctx := context.Background() 221 | tx := MustTx(boil.BeginTx(ctx, nil)) 222 | defer func() { _ = tx.Rollback() }() 223 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 224 | t.Error(err) 225 | } 226 | 227 | if x, err := PatientNotes().One(ctx, tx); err != nil { 228 | t.Error(err) 229 | } else if x == nil { 230 | t.Error("expected to get a non nil record") 231 | } 232 | } 233 | 234 | func testPatientNotesAll(t *testing.T) { 235 | t.Parallel() 236 | 237 | seed := randomize.NewSeed() 238 | var err error 239 | patientNoteOne := &PatientNote{} 240 | patientNoteTwo := &PatientNote{} 241 | if err = randomize.Struct(seed, patientNoteOne, patientNoteDBTypes, false, patientNoteColumnsWithDefault...); err != nil { 242 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 243 | } 244 | if err = randomize.Struct(seed, patientNoteTwo, patientNoteDBTypes, false, patientNoteColumnsWithDefault...); err != nil { 245 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 246 | } 247 | 248 | ctx := context.Background() 249 | tx := MustTx(boil.BeginTx(ctx, nil)) 250 | defer func() { _ = tx.Rollback() }() 251 | if err = patientNoteOne.Insert(ctx, tx, boil.Infer()); err != nil { 252 | t.Error(err) 253 | } 254 | if err = patientNoteTwo.Insert(ctx, tx, boil.Infer()); err != nil { 255 | t.Error(err) 256 | } 257 | 258 | slice, err := PatientNotes().All(ctx, tx) 259 | if err != nil { 260 | t.Error(err) 261 | } 262 | 263 | if len(slice) != 2 { 264 | t.Error("want 2 records, got:", len(slice)) 265 | } 266 | } 267 | 268 | func testPatientNotesCount(t *testing.T) { 269 | t.Parallel() 270 | 271 | var err error 272 | seed := randomize.NewSeed() 273 | patientNoteOne := &PatientNote{} 274 | patientNoteTwo := &PatientNote{} 275 | if err = randomize.Struct(seed, patientNoteOne, patientNoteDBTypes, false, patientNoteColumnsWithDefault...); err != nil { 276 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 277 | } 278 | if err = randomize.Struct(seed, patientNoteTwo, patientNoteDBTypes, false, patientNoteColumnsWithDefault...); err != nil { 279 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 280 | } 281 | 282 | ctx := context.Background() 283 | tx := MustTx(boil.BeginTx(ctx, nil)) 284 | defer func() { _ = tx.Rollback() }() 285 | if err = patientNoteOne.Insert(ctx, tx, boil.Infer()); err != nil { 286 | t.Error(err) 287 | } 288 | if err = patientNoteTwo.Insert(ctx, tx, boil.Infer()); err != nil { 289 | t.Error(err) 290 | } 291 | 292 | count, err := PatientNotes().Count(ctx, tx) 293 | if err != nil { 294 | t.Error(err) 295 | } 296 | 297 | if count != 2 { 298 | t.Error("want 2 records, got:", count) 299 | } 300 | } 301 | 302 | func patientNoteBeforeInsertHook(ctx context.Context, e boil.ContextExecutor, o *PatientNote) error { 303 | *o = PatientNote{} 304 | return nil 305 | } 306 | 307 | func patientNoteAfterInsertHook(ctx context.Context, e boil.ContextExecutor, o *PatientNote) error { 308 | *o = PatientNote{} 309 | return nil 310 | } 311 | 312 | func patientNoteAfterSelectHook(ctx context.Context, e boil.ContextExecutor, o *PatientNote) error { 313 | *o = PatientNote{} 314 | return nil 315 | } 316 | 317 | func patientNoteBeforeUpdateHook(ctx context.Context, e boil.ContextExecutor, o *PatientNote) error { 318 | *o = PatientNote{} 319 | return nil 320 | } 321 | 322 | func patientNoteAfterUpdateHook(ctx context.Context, e boil.ContextExecutor, o *PatientNote) error { 323 | *o = PatientNote{} 324 | return nil 325 | } 326 | 327 | func patientNoteBeforeDeleteHook(ctx context.Context, e boil.ContextExecutor, o *PatientNote) error { 328 | *o = PatientNote{} 329 | return nil 330 | } 331 | 332 | func patientNoteAfterDeleteHook(ctx context.Context, e boil.ContextExecutor, o *PatientNote) error { 333 | *o = PatientNote{} 334 | return nil 335 | } 336 | 337 | func patientNoteBeforeUpsertHook(ctx context.Context, e boil.ContextExecutor, o *PatientNote) error { 338 | *o = PatientNote{} 339 | return nil 340 | } 341 | 342 | func patientNoteAfterUpsertHook(ctx context.Context, e boil.ContextExecutor, o *PatientNote) error { 343 | *o = PatientNote{} 344 | return nil 345 | } 346 | 347 | func testPatientNotesHooks(t *testing.T) { 348 | t.Parallel() 349 | 350 | var err error 351 | 352 | ctx := context.Background() 353 | empty := &PatientNote{} 354 | o := &PatientNote{} 355 | 356 | seed := randomize.NewSeed() 357 | if err = randomize.Struct(seed, o, patientNoteDBTypes, false); err != nil { 358 | t.Errorf("Unable to randomize PatientNote object: %s", err) 359 | } 360 | 361 | AddPatientNoteHook(boil.BeforeInsertHook, patientNoteBeforeInsertHook) 362 | if err = o.doBeforeInsertHooks(ctx, nil); err != nil { 363 | t.Errorf("Unable to execute doBeforeInsertHooks: %s", err) 364 | } 365 | if !reflect.DeepEqual(o, empty) { 366 | t.Errorf("Expected BeforeInsertHook function to empty object, but got: %#v", o) 367 | } 368 | patientNoteBeforeInsertHooks = []PatientNoteHook{} 369 | 370 | AddPatientNoteHook(boil.AfterInsertHook, patientNoteAfterInsertHook) 371 | if err = o.doAfterInsertHooks(ctx, nil); err != nil { 372 | t.Errorf("Unable to execute doAfterInsertHooks: %s", err) 373 | } 374 | if !reflect.DeepEqual(o, empty) { 375 | t.Errorf("Expected AfterInsertHook function to empty object, but got: %#v", o) 376 | } 377 | patientNoteAfterInsertHooks = []PatientNoteHook{} 378 | 379 | AddPatientNoteHook(boil.AfterSelectHook, patientNoteAfterSelectHook) 380 | if err = o.doAfterSelectHooks(ctx, nil); err != nil { 381 | t.Errorf("Unable to execute doAfterSelectHooks: %s", err) 382 | } 383 | if !reflect.DeepEqual(o, empty) { 384 | t.Errorf("Expected AfterSelectHook function to empty object, but got: %#v", o) 385 | } 386 | patientNoteAfterSelectHooks = []PatientNoteHook{} 387 | 388 | AddPatientNoteHook(boil.BeforeUpdateHook, patientNoteBeforeUpdateHook) 389 | if err = o.doBeforeUpdateHooks(ctx, nil); err != nil { 390 | t.Errorf("Unable to execute doBeforeUpdateHooks: %s", err) 391 | } 392 | if !reflect.DeepEqual(o, empty) { 393 | t.Errorf("Expected BeforeUpdateHook function to empty object, but got: %#v", o) 394 | } 395 | patientNoteBeforeUpdateHooks = []PatientNoteHook{} 396 | 397 | AddPatientNoteHook(boil.AfterUpdateHook, patientNoteAfterUpdateHook) 398 | if err = o.doAfterUpdateHooks(ctx, nil); err != nil { 399 | t.Errorf("Unable to execute doAfterUpdateHooks: %s", err) 400 | } 401 | if !reflect.DeepEqual(o, empty) { 402 | t.Errorf("Expected AfterUpdateHook function to empty object, but got: %#v", o) 403 | } 404 | patientNoteAfterUpdateHooks = []PatientNoteHook{} 405 | 406 | AddPatientNoteHook(boil.BeforeDeleteHook, patientNoteBeforeDeleteHook) 407 | if err = o.doBeforeDeleteHooks(ctx, nil); err != nil { 408 | t.Errorf("Unable to execute doBeforeDeleteHooks: %s", err) 409 | } 410 | if !reflect.DeepEqual(o, empty) { 411 | t.Errorf("Expected BeforeDeleteHook function to empty object, but got: %#v", o) 412 | } 413 | patientNoteBeforeDeleteHooks = []PatientNoteHook{} 414 | 415 | AddPatientNoteHook(boil.AfterDeleteHook, patientNoteAfterDeleteHook) 416 | if err = o.doAfterDeleteHooks(ctx, nil); err != nil { 417 | t.Errorf("Unable to execute doAfterDeleteHooks: %s", err) 418 | } 419 | if !reflect.DeepEqual(o, empty) { 420 | t.Errorf("Expected AfterDeleteHook function to empty object, but got: %#v", o) 421 | } 422 | patientNoteAfterDeleteHooks = []PatientNoteHook{} 423 | 424 | AddPatientNoteHook(boil.BeforeUpsertHook, patientNoteBeforeUpsertHook) 425 | if err = o.doBeforeUpsertHooks(ctx, nil); err != nil { 426 | t.Errorf("Unable to execute doBeforeUpsertHooks: %s", err) 427 | } 428 | if !reflect.DeepEqual(o, empty) { 429 | t.Errorf("Expected BeforeUpsertHook function to empty object, but got: %#v", o) 430 | } 431 | patientNoteBeforeUpsertHooks = []PatientNoteHook{} 432 | 433 | AddPatientNoteHook(boil.AfterUpsertHook, patientNoteAfterUpsertHook) 434 | if err = o.doAfterUpsertHooks(ctx, nil); err != nil { 435 | t.Errorf("Unable to execute doAfterUpsertHooks: %s", err) 436 | } 437 | if !reflect.DeepEqual(o, empty) { 438 | t.Errorf("Expected AfterUpsertHook function to empty object, but got: %#v", o) 439 | } 440 | patientNoteAfterUpsertHooks = []PatientNoteHook{} 441 | } 442 | 443 | func testPatientNotesInsert(t *testing.T) { 444 | t.Parallel() 445 | 446 | seed := randomize.NewSeed() 447 | var err error 448 | o := &PatientNote{} 449 | if err = randomize.Struct(seed, o, patientNoteDBTypes, true, patientNoteColumnsWithDefault...); err != nil { 450 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 451 | } 452 | 453 | ctx := context.Background() 454 | tx := MustTx(boil.BeginTx(ctx, nil)) 455 | defer func() { _ = tx.Rollback() }() 456 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 457 | t.Error(err) 458 | } 459 | 460 | count, err := PatientNotes().Count(ctx, tx) 461 | if err != nil { 462 | t.Error(err) 463 | } 464 | 465 | if count != 1 { 466 | t.Error("want one record, got:", count) 467 | } 468 | } 469 | 470 | func testPatientNotesInsertWhitelist(t *testing.T) { 471 | t.Parallel() 472 | 473 | seed := randomize.NewSeed() 474 | var err error 475 | o := &PatientNote{} 476 | if err = randomize.Struct(seed, o, patientNoteDBTypes, true); err != nil { 477 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 478 | } 479 | 480 | ctx := context.Background() 481 | tx := MustTx(boil.BeginTx(ctx, nil)) 482 | defer func() { _ = tx.Rollback() }() 483 | if err = o.Insert(ctx, tx, boil.Whitelist(patientNoteColumnsWithoutDefault...)); err != nil { 484 | t.Error(err) 485 | } 486 | 487 | count, err := PatientNotes().Count(ctx, tx) 488 | if err != nil { 489 | t.Error(err) 490 | } 491 | 492 | if count != 1 { 493 | t.Error("want one record, got:", count) 494 | } 495 | } 496 | 497 | func testPatientNoteToOnePatientUsingPatientid(t *testing.T) { 498 | ctx := context.Background() 499 | tx := MustTx(boil.BeginTx(ctx, nil)) 500 | defer func() { _ = tx.Rollback() }() 501 | 502 | var local PatientNote 503 | var foreign Patient 504 | 505 | seed := randomize.NewSeed() 506 | if err := randomize.Struct(seed, &local, patientNoteDBTypes, false, patientNoteColumnsWithDefault...); err != nil { 507 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 508 | } 509 | if err := randomize.Struct(seed, &foreign, patientDBTypes, false, patientColumnsWithDefault...); err != nil { 510 | t.Errorf("Unable to randomize Patient struct: %s", err) 511 | } 512 | 513 | if err := foreign.Insert(ctx, tx, boil.Infer()); err != nil { 514 | t.Fatal(err) 515 | } 516 | 517 | local.Patient_Id = foreign.Patientid 518 | if err := local.Insert(ctx, tx, boil.Infer()); err != nil { 519 | t.Fatal(err) 520 | } 521 | 522 | check, err := local.Patientid().One(ctx, tx) 523 | if err != nil { 524 | t.Fatal(err) 525 | } 526 | 527 | if check.Patientid != foreign.Patientid { 528 | t.Errorf("want: %v, got %v", foreign.Patientid, check.Patientid) 529 | } 530 | 531 | slice := PatientNoteSlice{&local} 532 | if err = local.L.LoadPatientid(ctx, tx, false, (*[]*PatientNote)(&slice), nil); err != nil { 533 | t.Fatal(err) 534 | } 535 | if local.R.Patientid == nil { 536 | t.Error("struct should have been eager loaded") 537 | } 538 | 539 | local.R.Patientid = nil 540 | if err = local.L.LoadPatientid(ctx, tx, true, &local, nil); err != nil { 541 | t.Fatal(err) 542 | } 543 | if local.R.Patientid == nil { 544 | t.Error("struct should have been eager loaded") 545 | } 546 | } 547 | 548 | func testPatientNoteToOneUserUsingUserid(t *testing.T) { 549 | ctx := context.Background() 550 | tx := MustTx(boil.BeginTx(ctx, nil)) 551 | defer func() { _ = tx.Rollback() }() 552 | 553 | var local PatientNote 554 | var foreign User 555 | 556 | seed := randomize.NewSeed() 557 | if err := randomize.Struct(seed, &local, patientNoteDBTypes, false, patientNoteColumnsWithDefault...); err != nil { 558 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 559 | } 560 | if err := randomize.Struct(seed, &foreign, userDBTypes, false, userColumnsWithDefault...); err != nil { 561 | t.Errorf("Unable to randomize User struct: %s", err) 562 | } 563 | 564 | if err := foreign.Insert(ctx, tx, boil.Infer()); err != nil { 565 | t.Fatal(err) 566 | } 567 | 568 | local.User_Id = foreign.Userid 569 | if err := local.Insert(ctx, tx, boil.Infer()); err != nil { 570 | t.Fatal(err) 571 | } 572 | 573 | check, err := local.Userid().One(ctx, tx) 574 | if err != nil { 575 | t.Fatal(err) 576 | } 577 | 578 | if check.Userid != foreign.Userid { 579 | t.Errorf("want: %v, got %v", foreign.Userid, check.Userid) 580 | } 581 | 582 | slice := PatientNoteSlice{&local} 583 | if err = local.L.LoadUserid(ctx, tx, false, (*[]*PatientNote)(&slice), nil); err != nil { 584 | t.Fatal(err) 585 | } 586 | if local.R.Userid == nil { 587 | t.Error("struct should have been eager loaded") 588 | } 589 | 590 | local.R.Userid = nil 591 | if err = local.L.LoadUserid(ctx, tx, true, &local, nil); err != nil { 592 | t.Fatal(err) 593 | } 594 | if local.R.Userid == nil { 595 | t.Error("struct should have been eager loaded") 596 | } 597 | } 598 | 599 | func testPatientNoteToOneSetOpPatientUsingPatientid(t *testing.T) { 600 | var err error 601 | 602 | ctx := context.Background() 603 | tx := MustTx(boil.BeginTx(ctx, nil)) 604 | defer func() { _ = tx.Rollback() }() 605 | 606 | var a PatientNote 607 | var b, c Patient 608 | 609 | seed := randomize.NewSeed() 610 | if err = randomize.Struct(seed, &a, patientNoteDBTypes, false, strmangle.SetComplement(patientNotePrimaryKeyColumns, patientNoteColumnsWithoutDefault)...); err != nil { 611 | t.Fatal(err) 612 | } 613 | if err = randomize.Struct(seed, &b, patientDBTypes, false, strmangle.SetComplement(patientPrimaryKeyColumns, patientColumnsWithoutDefault)...); err != nil { 614 | t.Fatal(err) 615 | } 616 | if err = randomize.Struct(seed, &c, patientDBTypes, false, strmangle.SetComplement(patientPrimaryKeyColumns, patientColumnsWithoutDefault)...); err != nil { 617 | t.Fatal(err) 618 | } 619 | 620 | if err := a.Insert(ctx, tx, boil.Infer()); err != nil { 621 | t.Fatal(err) 622 | } 623 | if err = b.Insert(ctx, tx, boil.Infer()); err != nil { 624 | t.Fatal(err) 625 | } 626 | 627 | for i, x := range []*Patient{&b, &c} { 628 | err = a.SetPatientid(ctx, tx, i != 0, x) 629 | if err != nil { 630 | t.Fatal(err) 631 | } 632 | 633 | if a.R.Patientid != x { 634 | t.Error("relationship struct not set to correct value") 635 | } 636 | 637 | if x.R.PatientidPatientNotes[0] != &a { 638 | t.Error("failed to append to foreign relationship struct") 639 | } 640 | if a.Patient_Id != x.Patientid { 641 | t.Error("foreign key was wrong value", a.Patient_Id) 642 | } 643 | 644 | zero := reflect.Zero(reflect.TypeOf(a.Patient_Id)) 645 | reflect.Indirect(reflect.ValueOf(&a.Patient_Id)).Set(zero) 646 | 647 | if err = a.Reload(ctx, tx); err != nil { 648 | t.Fatal("failed to reload", err) 649 | } 650 | 651 | if a.Patient_Id != x.Patientid { 652 | t.Error("foreign key was wrong value", a.Patient_Id, x.Patientid) 653 | } 654 | } 655 | } 656 | func testPatientNoteToOneSetOpUserUsingUserid(t *testing.T) { 657 | var err error 658 | 659 | ctx := context.Background() 660 | tx := MustTx(boil.BeginTx(ctx, nil)) 661 | defer func() { _ = tx.Rollback() }() 662 | 663 | var a PatientNote 664 | var b, c User 665 | 666 | seed := randomize.NewSeed() 667 | if err = randomize.Struct(seed, &a, patientNoteDBTypes, false, strmangle.SetComplement(patientNotePrimaryKeyColumns, patientNoteColumnsWithoutDefault)...); err != nil { 668 | t.Fatal(err) 669 | } 670 | if err = randomize.Struct(seed, &b, userDBTypes, false, strmangle.SetComplement(userPrimaryKeyColumns, userColumnsWithoutDefault)...); err != nil { 671 | t.Fatal(err) 672 | } 673 | if err = randomize.Struct(seed, &c, userDBTypes, false, strmangle.SetComplement(userPrimaryKeyColumns, userColumnsWithoutDefault)...); err != nil { 674 | t.Fatal(err) 675 | } 676 | 677 | if err := a.Insert(ctx, tx, boil.Infer()); err != nil { 678 | t.Fatal(err) 679 | } 680 | if err = b.Insert(ctx, tx, boil.Infer()); err != nil { 681 | t.Fatal(err) 682 | } 683 | 684 | for i, x := range []*User{&b, &c} { 685 | err = a.SetUserid(ctx, tx, i != 0, x) 686 | if err != nil { 687 | t.Fatal(err) 688 | } 689 | 690 | if a.R.Userid != x { 691 | t.Error("relationship struct not set to correct value") 692 | } 693 | 694 | if x.R.UseridPatientNotes[0] != &a { 695 | t.Error("failed to append to foreign relationship struct") 696 | } 697 | if a.User_Id != x.Userid { 698 | t.Error("foreign key was wrong value", a.User_Id) 699 | } 700 | 701 | zero := reflect.Zero(reflect.TypeOf(a.User_Id)) 702 | reflect.Indirect(reflect.ValueOf(&a.User_Id)).Set(zero) 703 | 704 | if err = a.Reload(ctx, tx); err != nil { 705 | t.Fatal("failed to reload", err) 706 | } 707 | 708 | if a.User_Id != x.Userid { 709 | t.Error("foreign key was wrong value", a.User_Id, x.Userid) 710 | } 711 | } 712 | } 713 | 714 | func testPatientNotesReload(t *testing.T) { 715 | t.Parallel() 716 | 717 | seed := randomize.NewSeed() 718 | var err error 719 | o := &PatientNote{} 720 | if err = randomize.Struct(seed, o, patientNoteDBTypes, true, patientNoteColumnsWithDefault...); err != nil { 721 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 722 | } 723 | 724 | ctx := context.Background() 725 | tx := MustTx(boil.BeginTx(ctx, nil)) 726 | defer func() { _ = tx.Rollback() }() 727 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 728 | t.Error(err) 729 | } 730 | 731 | if err = o.Reload(ctx, tx); err != nil { 732 | t.Error(err) 733 | } 734 | } 735 | 736 | func testPatientNotesReloadAll(t *testing.T) { 737 | t.Parallel() 738 | 739 | seed := randomize.NewSeed() 740 | var err error 741 | o := &PatientNote{} 742 | if err = randomize.Struct(seed, o, patientNoteDBTypes, true, patientNoteColumnsWithDefault...); err != nil { 743 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 744 | } 745 | 746 | ctx := context.Background() 747 | tx := MustTx(boil.BeginTx(ctx, nil)) 748 | defer func() { _ = tx.Rollback() }() 749 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 750 | t.Error(err) 751 | } 752 | 753 | slice := PatientNoteSlice{o} 754 | 755 | if err = slice.ReloadAll(ctx, tx); err != nil { 756 | t.Error(err) 757 | } 758 | } 759 | 760 | func testPatientNotesSelect(t *testing.T) { 761 | t.Parallel() 762 | 763 | seed := randomize.NewSeed() 764 | var err error 765 | o := &PatientNote{} 766 | if err = randomize.Struct(seed, o, patientNoteDBTypes, true, patientNoteColumnsWithDefault...); err != nil { 767 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 768 | } 769 | 770 | ctx := context.Background() 771 | tx := MustTx(boil.BeginTx(ctx, nil)) 772 | defer func() { _ = tx.Rollback() }() 773 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 774 | t.Error(err) 775 | } 776 | 777 | slice, err := PatientNotes().All(ctx, tx) 778 | if err != nil { 779 | t.Error(err) 780 | } 781 | 782 | if len(slice) != 1 { 783 | t.Error("want one record, got:", len(slice)) 784 | } 785 | } 786 | 787 | var ( 788 | patientNoteDBTypes = map[string]string{`Noteid`: `character`, `Patient_Id`: `character varying`, `User_Id`: `character varying`, `Note`: `character varying`, `Created`: `timestamp with time zone`} 789 | _ = bytes.MinRead 790 | ) 791 | 792 | func testPatientNotesUpdate(t *testing.T) { 793 | t.Parallel() 794 | 795 | if 0 == len(patientNotePrimaryKeyColumns) { 796 | t.Skip("Skipping table with no primary key columns") 797 | } 798 | if len(patientNoteAllColumns) == len(patientNotePrimaryKeyColumns) { 799 | t.Skip("Skipping table with only primary key columns") 800 | } 801 | 802 | seed := randomize.NewSeed() 803 | var err error 804 | o := &PatientNote{} 805 | if err = randomize.Struct(seed, o, patientNoteDBTypes, true, patientNoteColumnsWithDefault...); err != nil { 806 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 807 | } 808 | 809 | ctx := context.Background() 810 | tx := MustTx(boil.BeginTx(ctx, nil)) 811 | defer func() { _ = tx.Rollback() }() 812 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 813 | t.Error(err) 814 | } 815 | 816 | count, err := PatientNotes().Count(ctx, tx) 817 | if err != nil { 818 | t.Error(err) 819 | } 820 | 821 | if count != 1 { 822 | t.Error("want one record, got:", count) 823 | } 824 | 825 | if err = randomize.Struct(seed, o, patientNoteDBTypes, true, patientNotePrimaryKeyColumns...); err != nil { 826 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 827 | } 828 | 829 | if rowsAff, err := o.Update(ctx, tx, boil.Infer()); err != nil { 830 | t.Error(err) 831 | } else if rowsAff != 1 { 832 | t.Error("should only affect one row but affected", rowsAff) 833 | } 834 | } 835 | 836 | func testPatientNotesSliceUpdateAll(t *testing.T) { 837 | t.Parallel() 838 | 839 | if len(patientNoteAllColumns) == len(patientNotePrimaryKeyColumns) { 840 | t.Skip("Skipping table with only primary key columns") 841 | } 842 | 843 | seed := randomize.NewSeed() 844 | var err error 845 | o := &PatientNote{} 846 | if err = randomize.Struct(seed, o, patientNoteDBTypes, true, patientNoteColumnsWithDefault...); err != nil { 847 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 848 | } 849 | 850 | ctx := context.Background() 851 | tx := MustTx(boil.BeginTx(ctx, nil)) 852 | defer func() { _ = tx.Rollback() }() 853 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 854 | t.Error(err) 855 | } 856 | 857 | count, err := PatientNotes().Count(ctx, tx) 858 | if err != nil { 859 | t.Error(err) 860 | } 861 | 862 | if count != 1 { 863 | t.Error("want one record, got:", count) 864 | } 865 | 866 | if err = randomize.Struct(seed, o, patientNoteDBTypes, true, patientNotePrimaryKeyColumns...); err != nil { 867 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 868 | } 869 | 870 | // Remove Primary keys and unique columns from what we plan to update 871 | var fields []string 872 | if strmangle.StringSliceMatch(patientNoteAllColumns, patientNotePrimaryKeyColumns) { 873 | fields = patientNoteAllColumns 874 | } else { 875 | fields = strmangle.SetComplement( 876 | patientNoteAllColumns, 877 | patientNotePrimaryKeyColumns, 878 | ) 879 | } 880 | 881 | value := reflect.Indirect(reflect.ValueOf(o)) 882 | typ := reflect.TypeOf(o).Elem() 883 | n := typ.NumField() 884 | 885 | updateMap := M{} 886 | for _, col := range fields { 887 | for i := 0; i < n; i++ { 888 | f := typ.Field(i) 889 | if f.Tag.Get("boil") == col { 890 | updateMap[col] = value.Field(i).Interface() 891 | } 892 | } 893 | } 894 | 895 | slice := PatientNoteSlice{o} 896 | if rowsAff, err := slice.UpdateAll(ctx, tx, updateMap); err != nil { 897 | t.Error(err) 898 | } else if rowsAff != 1 { 899 | t.Error("wanted one record updated but got", rowsAff) 900 | } 901 | } 902 | 903 | func testPatientNotesUpsert(t *testing.T) { 904 | t.Parallel() 905 | 906 | if len(patientNoteAllColumns) == len(patientNotePrimaryKeyColumns) { 907 | t.Skip("Skipping table with only primary key columns") 908 | } 909 | 910 | seed := randomize.NewSeed() 911 | var err error 912 | // Attempt the INSERT side of an UPSERT 913 | o := PatientNote{} 914 | if err = randomize.Struct(seed, &o, patientNoteDBTypes, true); err != nil { 915 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 916 | } 917 | 918 | ctx := context.Background() 919 | tx := MustTx(boil.BeginTx(ctx, nil)) 920 | defer func() { _ = tx.Rollback() }() 921 | if err = o.Upsert(ctx, tx, false, nil, boil.Infer(), boil.Infer()); err != nil { 922 | t.Errorf("Unable to upsert PatientNote: %s", err) 923 | } 924 | 925 | count, err := PatientNotes().Count(ctx, tx) 926 | if err != nil { 927 | t.Error(err) 928 | } 929 | if count != 1 { 930 | t.Error("want one record, got:", count) 931 | } 932 | 933 | // Attempt the UPDATE side of an UPSERT 934 | if err = randomize.Struct(seed, &o, patientNoteDBTypes, false, patientNotePrimaryKeyColumns...); err != nil { 935 | t.Errorf("Unable to randomize PatientNote struct: %s", err) 936 | } 937 | 938 | if err = o.Upsert(ctx, tx, true, nil, boil.Infer(), boil.Infer()); err != nil { 939 | t.Errorf("Unable to upsert PatientNote: %s", err) 940 | } 941 | 942 | count, err = PatientNotes().Count(ctx, tx) 943 | if err != nil { 944 | t.Error(err) 945 | } 946 | if count != 1 { 947 | t.Error("want one record, got:", count) 948 | } 949 | } 950 | -------------------------------------------------------------------------------- /backend/patientdb/models/patient_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 3.7.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package models 5 | 6 | import ( 7 | "bytes" 8 | "context" 9 | "reflect" 10 | "testing" 11 | 12 | "github.com/volatiletech/sqlboiler/boil" 13 | "github.com/volatiletech/sqlboiler/queries" 14 | "github.com/volatiletech/sqlboiler/randomize" 15 | "github.com/volatiletech/sqlboiler/strmangle" 16 | ) 17 | 18 | var ( 19 | // Relationships sometimes use the reflection helper queries.Equal/queries.Assign 20 | // so force a package dependency in case they don't. 21 | _ = queries.Equal 22 | ) 23 | 24 | func testPatients(t *testing.T) { 25 | t.Parallel() 26 | 27 | query := Patients() 28 | 29 | if query.Query == nil { 30 | t.Error("expected a query, got nothing") 31 | } 32 | } 33 | 34 | func testPatientsDelete(t *testing.T) { 35 | t.Parallel() 36 | 37 | seed := randomize.NewSeed() 38 | var err error 39 | o := &Patient{} 40 | if err = randomize.Struct(seed, o, patientDBTypes, true, patientColumnsWithDefault...); err != nil { 41 | t.Errorf("Unable to randomize Patient struct: %s", err) 42 | } 43 | 44 | ctx := context.Background() 45 | tx := MustTx(boil.BeginTx(ctx, nil)) 46 | defer func() { _ = tx.Rollback() }() 47 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 48 | t.Error(err) 49 | } 50 | 51 | if rowsAff, err := o.Delete(ctx, tx); err != nil { 52 | t.Error(err) 53 | } else if rowsAff != 1 { 54 | t.Error("should only have deleted one row, but affected:", rowsAff) 55 | } 56 | 57 | count, err := Patients().Count(ctx, tx) 58 | if err != nil { 59 | t.Error(err) 60 | } 61 | 62 | if count != 0 { 63 | t.Error("want zero records, got:", count) 64 | } 65 | } 66 | 67 | func testPatientsQueryDeleteAll(t *testing.T) { 68 | t.Parallel() 69 | 70 | seed := randomize.NewSeed() 71 | var err error 72 | o := &Patient{} 73 | if err = randomize.Struct(seed, o, patientDBTypes, true, patientColumnsWithDefault...); err != nil { 74 | t.Errorf("Unable to randomize Patient struct: %s", err) 75 | } 76 | 77 | ctx := context.Background() 78 | tx := MustTx(boil.BeginTx(ctx, nil)) 79 | defer func() { _ = tx.Rollback() }() 80 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 81 | t.Error(err) 82 | } 83 | 84 | if rowsAff, err := Patients().DeleteAll(ctx, tx); err != nil { 85 | t.Error(err) 86 | } else if rowsAff != 1 { 87 | t.Error("should only have deleted one row, but affected:", rowsAff) 88 | } 89 | 90 | count, err := Patients().Count(ctx, tx) 91 | if err != nil { 92 | t.Error(err) 93 | } 94 | 95 | if count != 0 { 96 | t.Error("want zero records, got:", count) 97 | } 98 | } 99 | 100 | func testPatientsSliceDeleteAll(t *testing.T) { 101 | t.Parallel() 102 | 103 | seed := randomize.NewSeed() 104 | var err error 105 | o := &Patient{} 106 | if err = randomize.Struct(seed, o, patientDBTypes, true, patientColumnsWithDefault...); err != nil { 107 | t.Errorf("Unable to randomize Patient struct: %s", err) 108 | } 109 | 110 | ctx := context.Background() 111 | tx := MustTx(boil.BeginTx(ctx, nil)) 112 | defer func() { _ = tx.Rollback() }() 113 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 114 | t.Error(err) 115 | } 116 | 117 | slice := PatientSlice{o} 118 | 119 | if rowsAff, err := slice.DeleteAll(ctx, tx); err != nil { 120 | t.Error(err) 121 | } else if rowsAff != 1 { 122 | t.Error("should only have deleted one row, but affected:", rowsAff) 123 | } 124 | 125 | count, err := Patients().Count(ctx, tx) 126 | if err != nil { 127 | t.Error(err) 128 | } 129 | 130 | if count != 0 { 131 | t.Error("want zero records, got:", count) 132 | } 133 | } 134 | 135 | func testPatientsExists(t *testing.T) { 136 | t.Parallel() 137 | 138 | seed := randomize.NewSeed() 139 | var err error 140 | o := &Patient{} 141 | if err = randomize.Struct(seed, o, patientDBTypes, true, patientColumnsWithDefault...); err != nil { 142 | t.Errorf("Unable to randomize Patient struct: %s", err) 143 | } 144 | 145 | ctx := context.Background() 146 | tx := MustTx(boil.BeginTx(ctx, nil)) 147 | defer func() { _ = tx.Rollback() }() 148 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 149 | t.Error(err) 150 | } 151 | 152 | e, err := PatientExists(ctx, tx, o.Patientid) 153 | if err != nil { 154 | t.Errorf("Unable to check if Patient exists: %s", err) 155 | } 156 | if !e { 157 | t.Errorf("Expected PatientExists to return true, but got false.") 158 | } 159 | } 160 | 161 | func testPatientsFind(t *testing.T) { 162 | t.Parallel() 163 | 164 | seed := randomize.NewSeed() 165 | var err error 166 | o := &Patient{} 167 | if err = randomize.Struct(seed, o, patientDBTypes, true, patientColumnsWithDefault...); err != nil { 168 | t.Errorf("Unable to randomize Patient struct: %s", err) 169 | } 170 | 171 | ctx := context.Background() 172 | tx := MustTx(boil.BeginTx(ctx, nil)) 173 | defer func() { _ = tx.Rollback() }() 174 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 175 | t.Error(err) 176 | } 177 | 178 | patientFound, err := FindPatient(ctx, tx, o.Patientid) 179 | if err != nil { 180 | t.Error(err) 181 | } 182 | 183 | if patientFound == nil { 184 | t.Error("want a record, got nil") 185 | } 186 | } 187 | 188 | func testPatientsBind(t *testing.T) { 189 | t.Parallel() 190 | 191 | seed := randomize.NewSeed() 192 | var err error 193 | o := &Patient{} 194 | if err = randomize.Struct(seed, o, patientDBTypes, true, patientColumnsWithDefault...); err != nil { 195 | t.Errorf("Unable to randomize Patient struct: %s", err) 196 | } 197 | 198 | ctx := context.Background() 199 | tx := MustTx(boil.BeginTx(ctx, nil)) 200 | defer func() { _ = tx.Rollback() }() 201 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 202 | t.Error(err) 203 | } 204 | 205 | if err = Patients().Bind(ctx, tx, o); err != nil { 206 | t.Error(err) 207 | } 208 | } 209 | 210 | func testPatientsOne(t *testing.T) { 211 | t.Parallel() 212 | 213 | seed := randomize.NewSeed() 214 | var err error 215 | o := &Patient{} 216 | if err = randomize.Struct(seed, o, patientDBTypes, true, patientColumnsWithDefault...); err != nil { 217 | t.Errorf("Unable to randomize Patient struct: %s", err) 218 | } 219 | 220 | ctx := context.Background() 221 | tx := MustTx(boil.BeginTx(ctx, nil)) 222 | defer func() { _ = tx.Rollback() }() 223 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 224 | t.Error(err) 225 | } 226 | 227 | if x, err := Patients().One(ctx, tx); err != nil { 228 | t.Error(err) 229 | } else if x == nil { 230 | t.Error("expected to get a non nil record") 231 | } 232 | } 233 | 234 | func testPatientsAll(t *testing.T) { 235 | t.Parallel() 236 | 237 | seed := randomize.NewSeed() 238 | var err error 239 | patientOne := &Patient{} 240 | patientTwo := &Patient{} 241 | if err = randomize.Struct(seed, patientOne, patientDBTypes, false, patientColumnsWithDefault...); err != nil { 242 | t.Errorf("Unable to randomize Patient struct: %s", err) 243 | } 244 | if err = randomize.Struct(seed, patientTwo, patientDBTypes, false, patientColumnsWithDefault...); err != nil { 245 | t.Errorf("Unable to randomize Patient struct: %s", err) 246 | } 247 | 248 | ctx := context.Background() 249 | tx := MustTx(boil.BeginTx(ctx, nil)) 250 | defer func() { _ = tx.Rollback() }() 251 | if err = patientOne.Insert(ctx, tx, boil.Infer()); err != nil { 252 | t.Error(err) 253 | } 254 | if err = patientTwo.Insert(ctx, tx, boil.Infer()); err != nil { 255 | t.Error(err) 256 | } 257 | 258 | slice, err := Patients().All(ctx, tx) 259 | if err != nil { 260 | t.Error(err) 261 | } 262 | 263 | if len(slice) != 2 { 264 | t.Error("want 2 records, got:", len(slice)) 265 | } 266 | } 267 | 268 | func testPatientsCount(t *testing.T) { 269 | t.Parallel() 270 | 271 | var err error 272 | seed := randomize.NewSeed() 273 | patientOne := &Patient{} 274 | patientTwo := &Patient{} 275 | if err = randomize.Struct(seed, patientOne, patientDBTypes, false, patientColumnsWithDefault...); err != nil { 276 | t.Errorf("Unable to randomize Patient struct: %s", err) 277 | } 278 | if err = randomize.Struct(seed, patientTwo, patientDBTypes, false, patientColumnsWithDefault...); err != nil { 279 | t.Errorf("Unable to randomize Patient struct: %s", err) 280 | } 281 | 282 | ctx := context.Background() 283 | tx := MustTx(boil.BeginTx(ctx, nil)) 284 | defer func() { _ = tx.Rollback() }() 285 | if err = patientOne.Insert(ctx, tx, boil.Infer()); err != nil { 286 | t.Error(err) 287 | } 288 | if err = patientTwo.Insert(ctx, tx, boil.Infer()); err != nil { 289 | t.Error(err) 290 | } 291 | 292 | count, err := Patients().Count(ctx, tx) 293 | if err != nil { 294 | t.Error(err) 295 | } 296 | 297 | if count != 2 { 298 | t.Error("want 2 records, got:", count) 299 | } 300 | } 301 | 302 | func patientBeforeInsertHook(ctx context.Context, e boil.ContextExecutor, o *Patient) error { 303 | *o = Patient{} 304 | return nil 305 | } 306 | 307 | func patientAfterInsertHook(ctx context.Context, e boil.ContextExecutor, o *Patient) error { 308 | *o = Patient{} 309 | return nil 310 | } 311 | 312 | func patientAfterSelectHook(ctx context.Context, e boil.ContextExecutor, o *Patient) error { 313 | *o = Patient{} 314 | return nil 315 | } 316 | 317 | func patientBeforeUpdateHook(ctx context.Context, e boil.ContextExecutor, o *Patient) error { 318 | *o = Patient{} 319 | return nil 320 | } 321 | 322 | func patientAfterUpdateHook(ctx context.Context, e boil.ContextExecutor, o *Patient) error { 323 | *o = Patient{} 324 | return nil 325 | } 326 | 327 | func patientBeforeDeleteHook(ctx context.Context, e boil.ContextExecutor, o *Patient) error { 328 | *o = Patient{} 329 | return nil 330 | } 331 | 332 | func patientAfterDeleteHook(ctx context.Context, e boil.ContextExecutor, o *Patient) error { 333 | *o = Patient{} 334 | return nil 335 | } 336 | 337 | func patientBeforeUpsertHook(ctx context.Context, e boil.ContextExecutor, o *Patient) error { 338 | *o = Patient{} 339 | return nil 340 | } 341 | 342 | func patientAfterUpsertHook(ctx context.Context, e boil.ContextExecutor, o *Patient) error { 343 | *o = Patient{} 344 | return nil 345 | } 346 | 347 | func testPatientsHooks(t *testing.T) { 348 | t.Parallel() 349 | 350 | var err error 351 | 352 | ctx := context.Background() 353 | empty := &Patient{} 354 | o := &Patient{} 355 | 356 | seed := randomize.NewSeed() 357 | if err = randomize.Struct(seed, o, patientDBTypes, false); err != nil { 358 | t.Errorf("Unable to randomize Patient object: %s", err) 359 | } 360 | 361 | AddPatientHook(boil.BeforeInsertHook, patientBeforeInsertHook) 362 | if err = o.doBeforeInsertHooks(ctx, nil); err != nil { 363 | t.Errorf("Unable to execute doBeforeInsertHooks: %s", err) 364 | } 365 | if !reflect.DeepEqual(o, empty) { 366 | t.Errorf("Expected BeforeInsertHook function to empty object, but got: %#v", o) 367 | } 368 | patientBeforeInsertHooks = []PatientHook{} 369 | 370 | AddPatientHook(boil.AfterInsertHook, patientAfterInsertHook) 371 | if err = o.doAfterInsertHooks(ctx, nil); err != nil { 372 | t.Errorf("Unable to execute doAfterInsertHooks: %s", err) 373 | } 374 | if !reflect.DeepEqual(o, empty) { 375 | t.Errorf("Expected AfterInsertHook function to empty object, but got: %#v", o) 376 | } 377 | patientAfterInsertHooks = []PatientHook{} 378 | 379 | AddPatientHook(boil.AfterSelectHook, patientAfterSelectHook) 380 | if err = o.doAfterSelectHooks(ctx, nil); err != nil { 381 | t.Errorf("Unable to execute doAfterSelectHooks: %s", err) 382 | } 383 | if !reflect.DeepEqual(o, empty) { 384 | t.Errorf("Expected AfterSelectHook function to empty object, but got: %#v", o) 385 | } 386 | patientAfterSelectHooks = []PatientHook{} 387 | 388 | AddPatientHook(boil.BeforeUpdateHook, patientBeforeUpdateHook) 389 | if err = o.doBeforeUpdateHooks(ctx, nil); err != nil { 390 | t.Errorf("Unable to execute doBeforeUpdateHooks: %s", err) 391 | } 392 | if !reflect.DeepEqual(o, empty) { 393 | t.Errorf("Expected BeforeUpdateHook function to empty object, but got: %#v", o) 394 | } 395 | patientBeforeUpdateHooks = []PatientHook{} 396 | 397 | AddPatientHook(boil.AfterUpdateHook, patientAfterUpdateHook) 398 | if err = o.doAfterUpdateHooks(ctx, nil); err != nil { 399 | t.Errorf("Unable to execute doAfterUpdateHooks: %s", err) 400 | } 401 | if !reflect.DeepEqual(o, empty) { 402 | t.Errorf("Expected AfterUpdateHook function to empty object, but got: %#v", o) 403 | } 404 | patientAfterUpdateHooks = []PatientHook{} 405 | 406 | AddPatientHook(boil.BeforeDeleteHook, patientBeforeDeleteHook) 407 | if err = o.doBeforeDeleteHooks(ctx, nil); err != nil { 408 | t.Errorf("Unable to execute doBeforeDeleteHooks: %s", err) 409 | } 410 | if !reflect.DeepEqual(o, empty) { 411 | t.Errorf("Expected BeforeDeleteHook function to empty object, but got: %#v", o) 412 | } 413 | patientBeforeDeleteHooks = []PatientHook{} 414 | 415 | AddPatientHook(boil.AfterDeleteHook, patientAfterDeleteHook) 416 | if err = o.doAfterDeleteHooks(ctx, nil); err != nil { 417 | t.Errorf("Unable to execute doAfterDeleteHooks: %s", err) 418 | } 419 | if !reflect.DeepEqual(o, empty) { 420 | t.Errorf("Expected AfterDeleteHook function to empty object, but got: %#v", o) 421 | } 422 | patientAfterDeleteHooks = []PatientHook{} 423 | 424 | AddPatientHook(boil.BeforeUpsertHook, patientBeforeUpsertHook) 425 | if err = o.doBeforeUpsertHooks(ctx, nil); err != nil { 426 | t.Errorf("Unable to execute doBeforeUpsertHooks: %s", err) 427 | } 428 | if !reflect.DeepEqual(o, empty) { 429 | t.Errorf("Expected BeforeUpsertHook function to empty object, but got: %#v", o) 430 | } 431 | patientBeforeUpsertHooks = []PatientHook{} 432 | 433 | AddPatientHook(boil.AfterUpsertHook, patientAfterUpsertHook) 434 | if err = o.doAfterUpsertHooks(ctx, nil); err != nil { 435 | t.Errorf("Unable to execute doAfterUpsertHooks: %s", err) 436 | } 437 | if !reflect.DeepEqual(o, empty) { 438 | t.Errorf("Expected AfterUpsertHook function to empty object, but got: %#v", o) 439 | } 440 | patientAfterUpsertHooks = []PatientHook{} 441 | } 442 | 443 | func testPatientsInsert(t *testing.T) { 444 | t.Parallel() 445 | 446 | seed := randomize.NewSeed() 447 | var err error 448 | o := &Patient{} 449 | if err = randomize.Struct(seed, o, patientDBTypes, true, patientColumnsWithDefault...); err != nil { 450 | t.Errorf("Unable to randomize Patient struct: %s", err) 451 | } 452 | 453 | ctx := context.Background() 454 | tx := MustTx(boil.BeginTx(ctx, nil)) 455 | defer func() { _ = tx.Rollback() }() 456 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 457 | t.Error(err) 458 | } 459 | 460 | count, err := Patients().Count(ctx, tx) 461 | if err != nil { 462 | t.Error(err) 463 | } 464 | 465 | if count != 1 { 466 | t.Error("want one record, got:", count) 467 | } 468 | } 469 | 470 | func testPatientsInsertWhitelist(t *testing.T) { 471 | t.Parallel() 472 | 473 | seed := randomize.NewSeed() 474 | var err error 475 | o := &Patient{} 476 | if err = randomize.Struct(seed, o, patientDBTypes, true); err != nil { 477 | t.Errorf("Unable to randomize Patient struct: %s", err) 478 | } 479 | 480 | ctx := context.Background() 481 | tx := MustTx(boil.BeginTx(ctx, nil)) 482 | defer func() { _ = tx.Rollback() }() 483 | if err = o.Insert(ctx, tx, boil.Whitelist(patientColumnsWithoutDefault...)); err != nil { 484 | t.Error(err) 485 | } 486 | 487 | count, err := Patients().Count(ctx, tx) 488 | if err != nil { 489 | t.Error(err) 490 | } 491 | 492 | if count != 1 { 493 | t.Error("want one record, got:", count) 494 | } 495 | } 496 | 497 | func testPatientToManyPatientidPatientNotes(t *testing.T) { 498 | var err error 499 | ctx := context.Background() 500 | tx := MustTx(boil.BeginTx(ctx, nil)) 501 | defer func() { _ = tx.Rollback() }() 502 | 503 | var a Patient 504 | var b, c PatientNote 505 | 506 | seed := randomize.NewSeed() 507 | if err = randomize.Struct(seed, &a, patientDBTypes, true, patientColumnsWithDefault...); err != nil { 508 | t.Errorf("Unable to randomize Patient struct: %s", err) 509 | } 510 | 511 | if err := a.Insert(ctx, tx, boil.Infer()); err != nil { 512 | t.Fatal(err) 513 | } 514 | 515 | if err = randomize.Struct(seed, &b, patientNoteDBTypes, false, patientNoteColumnsWithDefault...); err != nil { 516 | t.Fatal(err) 517 | } 518 | if err = randomize.Struct(seed, &c, patientNoteDBTypes, false, patientNoteColumnsWithDefault...); err != nil { 519 | t.Fatal(err) 520 | } 521 | 522 | b.Patient_Id = a.Patientid 523 | c.Patient_Id = a.Patientid 524 | 525 | if err = b.Insert(ctx, tx, boil.Infer()); err != nil { 526 | t.Fatal(err) 527 | } 528 | if err = c.Insert(ctx, tx, boil.Infer()); err != nil { 529 | t.Fatal(err) 530 | } 531 | 532 | check, err := a.PatientidPatientNotes().All(ctx, tx) 533 | if err != nil { 534 | t.Fatal(err) 535 | } 536 | 537 | bFound, cFound := false, false 538 | for _, v := range check { 539 | if v.Patient_Id == b.Patient_Id { 540 | bFound = true 541 | } 542 | if v.Patient_Id == c.Patient_Id { 543 | cFound = true 544 | } 545 | } 546 | 547 | if !bFound { 548 | t.Error("expected to find b") 549 | } 550 | if !cFound { 551 | t.Error("expected to find c") 552 | } 553 | 554 | slice := PatientSlice{&a} 555 | if err = a.L.LoadPatientidPatientNotes(ctx, tx, false, (*[]*Patient)(&slice), nil); err != nil { 556 | t.Fatal(err) 557 | } 558 | if got := len(a.R.PatientidPatientNotes); got != 2 { 559 | t.Error("number of eager loaded records wrong, got:", got) 560 | } 561 | 562 | a.R.PatientidPatientNotes = nil 563 | if err = a.L.LoadPatientidPatientNotes(ctx, tx, true, &a, nil); err != nil { 564 | t.Fatal(err) 565 | } 566 | if got := len(a.R.PatientidPatientNotes); got != 2 { 567 | t.Error("number of eager loaded records wrong, got:", got) 568 | } 569 | 570 | if t.Failed() { 571 | t.Logf("%#v", check) 572 | } 573 | } 574 | 575 | func testPatientToManyAddOpPatientidPatientNotes(t *testing.T) { 576 | var err error 577 | 578 | ctx := context.Background() 579 | tx := MustTx(boil.BeginTx(ctx, nil)) 580 | defer func() { _ = tx.Rollback() }() 581 | 582 | var a Patient 583 | var b, c, d, e PatientNote 584 | 585 | seed := randomize.NewSeed() 586 | if err = randomize.Struct(seed, &a, patientDBTypes, false, strmangle.SetComplement(patientPrimaryKeyColumns, patientColumnsWithoutDefault)...); err != nil { 587 | t.Fatal(err) 588 | } 589 | foreigners := []*PatientNote{&b, &c, &d, &e} 590 | for _, x := range foreigners { 591 | if err = randomize.Struct(seed, x, patientNoteDBTypes, false, strmangle.SetComplement(patientNotePrimaryKeyColumns, patientNoteColumnsWithoutDefault)...); err != nil { 592 | t.Fatal(err) 593 | } 594 | } 595 | 596 | if err := a.Insert(ctx, tx, boil.Infer()); err != nil { 597 | t.Fatal(err) 598 | } 599 | if err = b.Insert(ctx, tx, boil.Infer()); err != nil { 600 | t.Fatal(err) 601 | } 602 | if err = c.Insert(ctx, tx, boil.Infer()); err != nil { 603 | t.Fatal(err) 604 | } 605 | 606 | foreignersSplitByInsertion := [][]*PatientNote{ 607 | {&b, &c}, 608 | {&d, &e}, 609 | } 610 | 611 | for i, x := range foreignersSplitByInsertion { 612 | err = a.AddPatientidPatientNotes(ctx, tx, i != 0, x...) 613 | if err != nil { 614 | t.Fatal(err) 615 | } 616 | 617 | first := x[0] 618 | second := x[1] 619 | 620 | if a.Patientid != first.Patient_Id { 621 | t.Error("foreign key was wrong value", a.Patientid, first.Patient_Id) 622 | } 623 | if a.Patientid != second.Patient_Id { 624 | t.Error("foreign key was wrong value", a.Patientid, second.Patient_Id) 625 | } 626 | 627 | if first.R.Patientid != &a { 628 | t.Error("relationship was not added properly to the foreign slice") 629 | } 630 | if second.R.Patientid != &a { 631 | t.Error("relationship was not added properly to the foreign slice") 632 | } 633 | 634 | if a.R.PatientidPatientNotes[i*2] != first { 635 | t.Error("relationship struct slice not set to correct value") 636 | } 637 | if a.R.PatientidPatientNotes[i*2+1] != second { 638 | t.Error("relationship struct slice not set to correct value") 639 | } 640 | 641 | count, err := a.PatientidPatientNotes().Count(ctx, tx) 642 | if err != nil { 643 | t.Fatal(err) 644 | } 645 | if want := int64((i + 1) * 2); count != want { 646 | t.Error("want", want, "got", count) 647 | } 648 | } 649 | } 650 | 651 | func testPatientsReload(t *testing.T) { 652 | t.Parallel() 653 | 654 | seed := randomize.NewSeed() 655 | var err error 656 | o := &Patient{} 657 | if err = randomize.Struct(seed, o, patientDBTypes, true, patientColumnsWithDefault...); err != nil { 658 | t.Errorf("Unable to randomize Patient struct: %s", err) 659 | } 660 | 661 | ctx := context.Background() 662 | tx := MustTx(boil.BeginTx(ctx, nil)) 663 | defer func() { _ = tx.Rollback() }() 664 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 665 | t.Error(err) 666 | } 667 | 668 | if err = o.Reload(ctx, tx); err != nil { 669 | t.Error(err) 670 | } 671 | } 672 | 673 | func testPatientsReloadAll(t *testing.T) { 674 | t.Parallel() 675 | 676 | seed := randomize.NewSeed() 677 | var err error 678 | o := &Patient{} 679 | if err = randomize.Struct(seed, o, patientDBTypes, true, patientColumnsWithDefault...); err != nil { 680 | t.Errorf("Unable to randomize Patient struct: %s", err) 681 | } 682 | 683 | ctx := context.Background() 684 | tx := MustTx(boil.BeginTx(ctx, nil)) 685 | defer func() { _ = tx.Rollback() }() 686 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 687 | t.Error(err) 688 | } 689 | 690 | slice := PatientSlice{o} 691 | 692 | if err = slice.ReloadAll(ctx, tx); err != nil { 693 | t.Error(err) 694 | } 695 | } 696 | 697 | func testPatientsSelect(t *testing.T) { 698 | t.Parallel() 699 | 700 | seed := randomize.NewSeed() 701 | var err error 702 | o := &Patient{} 703 | if err = randomize.Struct(seed, o, patientDBTypes, true, patientColumnsWithDefault...); err != nil { 704 | t.Errorf("Unable to randomize Patient struct: %s", err) 705 | } 706 | 707 | ctx := context.Background() 708 | tx := MustTx(boil.BeginTx(ctx, nil)) 709 | defer func() { _ = tx.Rollback() }() 710 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 711 | t.Error(err) 712 | } 713 | 714 | slice, err := Patients().All(ctx, tx) 715 | if err != nil { 716 | t.Error(err) 717 | } 718 | 719 | if len(slice) != 1 { 720 | t.Error("want one record, got:", len(slice)) 721 | } 722 | } 723 | 724 | var ( 725 | patientDBTypes = map[string]string{`Patientid`: `character varying`, `Name`: `character varying`, `Location`: `character varying`, `Created`: `timestamp with time zone`} 726 | _ = bytes.MinRead 727 | ) 728 | 729 | func testPatientsUpdate(t *testing.T) { 730 | t.Parallel() 731 | 732 | if 0 == len(patientPrimaryKeyColumns) { 733 | t.Skip("Skipping table with no primary key columns") 734 | } 735 | if len(patientAllColumns) == len(patientPrimaryKeyColumns) { 736 | t.Skip("Skipping table with only primary key columns") 737 | } 738 | 739 | seed := randomize.NewSeed() 740 | var err error 741 | o := &Patient{} 742 | if err = randomize.Struct(seed, o, patientDBTypes, true, patientColumnsWithDefault...); err != nil { 743 | t.Errorf("Unable to randomize Patient struct: %s", err) 744 | } 745 | 746 | ctx := context.Background() 747 | tx := MustTx(boil.BeginTx(ctx, nil)) 748 | defer func() { _ = tx.Rollback() }() 749 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 750 | t.Error(err) 751 | } 752 | 753 | count, err := Patients().Count(ctx, tx) 754 | if err != nil { 755 | t.Error(err) 756 | } 757 | 758 | if count != 1 { 759 | t.Error("want one record, got:", count) 760 | } 761 | 762 | if err = randomize.Struct(seed, o, patientDBTypes, true, patientPrimaryKeyColumns...); err != nil { 763 | t.Errorf("Unable to randomize Patient struct: %s", err) 764 | } 765 | 766 | if rowsAff, err := o.Update(ctx, tx, boil.Infer()); err != nil { 767 | t.Error(err) 768 | } else if rowsAff != 1 { 769 | t.Error("should only affect one row but affected", rowsAff) 770 | } 771 | } 772 | 773 | func testPatientsSliceUpdateAll(t *testing.T) { 774 | t.Parallel() 775 | 776 | if len(patientAllColumns) == len(patientPrimaryKeyColumns) { 777 | t.Skip("Skipping table with only primary key columns") 778 | } 779 | 780 | seed := randomize.NewSeed() 781 | var err error 782 | o := &Patient{} 783 | if err = randomize.Struct(seed, o, patientDBTypes, true, patientColumnsWithDefault...); err != nil { 784 | t.Errorf("Unable to randomize Patient struct: %s", err) 785 | } 786 | 787 | ctx := context.Background() 788 | tx := MustTx(boil.BeginTx(ctx, nil)) 789 | defer func() { _ = tx.Rollback() }() 790 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 791 | t.Error(err) 792 | } 793 | 794 | count, err := Patients().Count(ctx, tx) 795 | if err != nil { 796 | t.Error(err) 797 | } 798 | 799 | if count != 1 { 800 | t.Error("want one record, got:", count) 801 | } 802 | 803 | if err = randomize.Struct(seed, o, patientDBTypes, true, patientPrimaryKeyColumns...); err != nil { 804 | t.Errorf("Unable to randomize Patient struct: %s", err) 805 | } 806 | 807 | // Remove Primary keys and unique columns from what we plan to update 808 | var fields []string 809 | if strmangle.StringSliceMatch(patientAllColumns, patientPrimaryKeyColumns) { 810 | fields = patientAllColumns 811 | } else { 812 | fields = strmangle.SetComplement( 813 | patientAllColumns, 814 | patientPrimaryKeyColumns, 815 | ) 816 | } 817 | 818 | value := reflect.Indirect(reflect.ValueOf(o)) 819 | typ := reflect.TypeOf(o).Elem() 820 | n := typ.NumField() 821 | 822 | updateMap := M{} 823 | for _, col := range fields { 824 | for i := 0; i < n; i++ { 825 | f := typ.Field(i) 826 | if f.Tag.Get("boil") == col { 827 | updateMap[col] = value.Field(i).Interface() 828 | } 829 | } 830 | } 831 | 832 | slice := PatientSlice{o} 833 | if rowsAff, err := slice.UpdateAll(ctx, tx, updateMap); err != nil { 834 | t.Error(err) 835 | } else if rowsAff != 1 { 836 | t.Error("wanted one record updated but got", rowsAff) 837 | } 838 | } 839 | 840 | func testPatientsUpsert(t *testing.T) { 841 | t.Parallel() 842 | 843 | if len(patientAllColumns) == len(patientPrimaryKeyColumns) { 844 | t.Skip("Skipping table with only primary key columns") 845 | } 846 | 847 | seed := randomize.NewSeed() 848 | var err error 849 | // Attempt the INSERT side of an UPSERT 850 | o := Patient{} 851 | if err = randomize.Struct(seed, &o, patientDBTypes, true); err != nil { 852 | t.Errorf("Unable to randomize Patient struct: %s", err) 853 | } 854 | 855 | ctx := context.Background() 856 | tx := MustTx(boil.BeginTx(ctx, nil)) 857 | defer func() { _ = tx.Rollback() }() 858 | if err = o.Upsert(ctx, tx, false, nil, boil.Infer(), boil.Infer()); err != nil { 859 | t.Errorf("Unable to upsert Patient: %s", err) 860 | } 861 | 862 | count, err := Patients().Count(ctx, tx) 863 | if err != nil { 864 | t.Error(err) 865 | } 866 | if count != 1 { 867 | t.Error("want one record, got:", count) 868 | } 869 | 870 | // Attempt the UPDATE side of an UPSERT 871 | if err = randomize.Struct(seed, &o, patientDBTypes, false, patientPrimaryKeyColumns...); err != nil { 872 | t.Errorf("Unable to randomize Patient struct: %s", err) 873 | } 874 | 875 | if err = o.Upsert(ctx, tx, true, nil, boil.Infer(), boil.Infer()); err != nil { 876 | t.Errorf("Unable to upsert Patient: %s", err) 877 | } 878 | 879 | count, err = Patients().Count(ctx, tx) 880 | if err != nil { 881 | t.Error(err) 882 | } 883 | if count != 1 { 884 | t.Error("want one record, got:", count) 885 | } 886 | } 887 | -------------------------------------------------------------------------------- /backend/patientdb/models/psql_main_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 3.7.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package models 5 | 6 | import ( 7 | "bytes" 8 | "database/sql" 9 | "fmt" 10 | "io" 11 | "io/ioutil" 12 | "os" 13 | "os/exec" 14 | "regexp" 15 | "strings" 16 | 17 | "github.com/friendsofgo/errors" 18 | "github.com/kat-co/vala" 19 | _ "github.com/lib/pq" 20 | "github.com/spf13/viper" 21 | "github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql/driver" 22 | "github.com/volatiletech/sqlboiler/randomize" 23 | ) 24 | 25 | var rgxPGFkey = regexp.MustCompile(`(?m)^ALTER TABLE ONLY .*\n\s+ADD CONSTRAINT .*? FOREIGN KEY .*?;\n`) 26 | 27 | type pgTester struct { 28 | dbConn *sql.DB 29 | 30 | dbName string 31 | host string 32 | user string 33 | pass string 34 | sslmode string 35 | port int 36 | 37 | pgPassFile string 38 | 39 | testDBName string 40 | skipSQLCmd bool 41 | } 42 | 43 | func init() { 44 | dbMain = &pgTester{} 45 | } 46 | 47 | // setup dumps the database schema and imports it into a temporary randomly 48 | // generated test database so that tests can be run against it using the 49 | // generated sqlboiler ORM package. 50 | func (p *pgTester) setup() error { 51 | var err error 52 | 53 | viper.SetDefault("psql.schema", "public") 54 | viper.SetDefault("psql.port", 5432) 55 | viper.SetDefault("psql.sslmode", "require") 56 | 57 | p.dbName = viper.GetString("psql.dbname") 58 | p.host = viper.GetString("psql.host") 59 | p.user = viper.GetString("psql.user") 60 | p.pass = viper.GetString("psql.pass") 61 | p.port = viper.GetInt("psql.port") 62 | p.sslmode = viper.GetString("psql.sslmode") 63 | p.testDBName = viper.GetString("psql.testdbname") 64 | p.skipSQLCmd = viper.GetBool("psql.skipsqlcmd") 65 | 66 | err = vala.BeginValidation().Validate( 67 | vala.StringNotEmpty(p.user, "psql.user"), 68 | vala.StringNotEmpty(p.host, "psql.host"), 69 | vala.Not(vala.Equals(p.port, 0, "psql.port")), 70 | vala.StringNotEmpty(p.dbName, "psql.dbname"), 71 | vala.StringNotEmpty(p.sslmode, "psql.sslmode"), 72 | ).Check() 73 | 74 | if err != nil { 75 | return err 76 | } 77 | 78 | // if no testing DB passed 79 | if len(p.testDBName) == 0 { 80 | // Create a randomized db name. 81 | p.testDBName = randomize.StableDBName(p.dbName) 82 | } 83 | 84 | if err = p.makePGPassFile(); err != nil { 85 | return err 86 | } 87 | 88 | if !p.skipSQLCmd { 89 | if err = p.dropTestDB(); err != nil { 90 | return err 91 | } 92 | if err = p.createTestDB(); err != nil { 93 | return err 94 | } 95 | 96 | dumpCmd := exec.Command("pg_dump", "--schema-only", p.dbName) 97 | dumpCmd.Env = append(os.Environ(), p.pgEnv()...) 98 | createCmd := exec.Command("psql", p.testDBName) 99 | createCmd.Env = append(os.Environ(), p.pgEnv()...) 100 | 101 | r, w := io.Pipe() 102 | dumpCmdStderr := &bytes.Buffer{} 103 | createCmdStderr := &bytes.Buffer{} 104 | 105 | dumpCmd.Stdout = w 106 | dumpCmd.Stderr = dumpCmdStderr 107 | 108 | createCmd.Stdin = newFKeyDestroyer(rgxPGFkey, r) 109 | createCmd.Stderr = createCmdStderr 110 | 111 | if err = dumpCmd.Start(); err != nil { 112 | return errors.Wrap(err, "failed to start pg_dump command") 113 | } 114 | if err = createCmd.Start(); err != nil { 115 | return errors.Wrap(err, "failed to start psql command") 116 | } 117 | 118 | if err = dumpCmd.Wait(); err != nil { 119 | fmt.Println(err) 120 | fmt.Println(dumpCmdStderr.String()) 121 | return errors.Wrap(err, "failed to wait for pg_dump command") 122 | } 123 | 124 | _ = w.Close() // After dumpCmd is done, close the write end of the pipe 125 | 126 | if err = createCmd.Wait(); err != nil { 127 | fmt.Println(err) 128 | fmt.Println(createCmdStderr.String()) 129 | return errors.Wrap(err, "failed to wait for psql command") 130 | } 131 | } 132 | 133 | return nil 134 | } 135 | 136 | func (p *pgTester) runCmd(stdin, command string, args ...string) error { 137 | cmd := exec.Command(command, args...) 138 | cmd.Env = append(os.Environ(), p.pgEnv()...) 139 | 140 | if len(stdin) != 0 { 141 | cmd.Stdin = strings.NewReader(stdin) 142 | } 143 | 144 | stdout := &bytes.Buffer{} 145 | stderr := &bytes.Buffer{} 146 | cmd.Stdout = stdout 147 | cmd.Stderr = stderr 148 | if err := cmd.Run(); err != nil { 149 | fmt.Println("failed running:", command, args) 150 | fmt.Println(stdout.String()) 151 | fmt.Println(stderr.String()) 152 | return err 153 | } 154 | 155 | return nil 156 | } 157 | 158 | func (p *pgTester) pgEnv() []string { 159 | return []string{ 160 | fmt.Sprintf("PGHOST=%s", p.host), 161 | fmt.Sprintf("PGPORT=%d", p.port), 162 | fmt.Sprintf("PGUSER=%s", p.user), 163 | fmt.Sprintf("PGPASSFILE=%s", p.pgPassFile), 164 | } 165 | } 166 | 167 | func (p *pgTester) makePGPassFile() error { 168 | tmp, err := ioutil.TempFile("", "pgpass") 169 | if err != nil { 170 | return errors.Wrap(err, "failed to create option file") 171 | } 172 | 173 | fmt.Fprintf(tmp, "%s:%d:postgres:%s", p.host, p.port, p.user) 174 | if len(p.pass) != 0 { 175 | fmt.Fprintf(tmp, ":%s", p.pass) 176 | } 177 | fmt.Fprintln(tmp) 178 | 179 | fmt.Fprintf(tmp, "%s:%d:%s:%s", p.host, p.port, p.dbName, p.user) 180 | if len(p.pass) != 0 { 181 | fmt.Fprintf(tmp, ":%s", p.pass) 182 | } 183 | fmt.Fprintln(tmp) 184 | 185 | fmt.Fprintf(tmp, "%s:%d:%s:%s", p.host, p.port, p.testDBName, p.user) 186 | if len(p.pass) != 0 { 187 | fmt.Fprintf(tmp, ":%s", p.pass) 188 | } 189 | fmt.Fprintln(tmp) 190 | 191 | p.pgPassFile = tmp.Name() 192 | return tmp.Close() 193 | } 194 | 195 | func (p *pgTester) createTestDB() error { 196 | return p.runCmd("", "createdb", p.testDBName) 197 | } 198 | 199 | func (p *pgTester) dropTestDB() error { 200 | return p.runCmd("", "dropdb", "--if-exists", p.testDBName) 201 | } 202 | 203 | // teardown executes cleanup tasks when the tests finish running 204 | func (p *pgTester) teardown() error { 205 | var err error 206 | if err = p.dbConn.Close(); err != nil { 207 | return err 208 | } 209 | p.dbConn = nil 210 | 211 | if !p.skipSQLCmd { 212 | if err = p.dropTestDB(); err != nil { 213 | return err 214 | } 215 | } 216 | 217 | return os.Remove(p.pgPassFile) 218 | } 219 | 220 | func (p *pgTester) conn() (*sql.DB, error) { 221 | if p.dbConn != nil { 222 | return p.dbConn, nil 223 | } 224 | 225 | var err error 226 | p.dbConn, err = sql.Open("postgres", driver.PSQLBuildQueryString(p.user, p.pass, p.testDBName, p.host, p.port, p.sslmode)) 227 | if err != nil { 228 | return nil, err 229 | } 230 | 231 | return p.dbConn, nil 232 | } 233 | -------------------------------------------------------------------------------- /backend/patientdb/models/psql_suites_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 3.7.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package models 5 | 6 | import "testing" 7 | 8 | func TestUpsert(t *testing.T) { 9 | t.Run("Patients", testPatientsUpsert) 10 | 11 | t.Run("PatientNotes", testPatientNotesUpsert) 12 | 13 | t.Run("SchemaMigrations", testSchemaMigrationsUpsert) 14 | 15 | t.Run("Users", testUsersUpsert) 16 | } 17 | -------------------------------------------------------------------------------- /backend/patientdb/models/psql_upsert.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 3.7.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package models 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | 10 | "github.com/volatiletech/sqlboiler/drivers" 11 | "github.com/volatiletech/sqlboiler/strmangle" 12 | ) 13 | 14 | // buildUpsertQueryPostgres builds a SQL statement string using the upsertData provided. 15 | func buildUpsertQueryPostgres(dia drivers.Dialect, tableName string, updateOnConflict bool, ret, update, conflict, whitelist []string) string { 16 | conflict = strmangle.IdentQuoteSlice(dia.LQ, dia.RQ, conflict) 17 | whitelist = strmangle.IdentQuoteSlice(dia.LQ, dia.RQ, whitelist) 18 | ret = strmangle.IdentQuoteSlice(dia.LQ, dia.RQ, ret) 19 | 20 | buf := strmangle.GetBuffer() 21 | defer strmangle.PutBuffer(buf) 22 | 23 | columns := "DEFAULT VALUES" 24 | if len(whitelist) != 0 { 25 | columns = fmt.Sprintf("(%s) VALUES (%s)", 26 | strings.Join(whitelist, ", "), 27 | strmangle.Placeholders(dia.UseIndexPlaceholders, len(whitelist), 1, 1)) 28 | } 29 | 30 | fmt.Fprintf( 31 | buf, 32 | "INSERT INTO %s %s ON CONFLICT ", 33 | tableName, 34 | columns, 35 | ) 36 | 37 | if !updateOnConflict || len(update) == 0 { 38 | buf.WriteString("DO NOTHING") 39 | } else { 40 | buf.WriteByte('(') 41 | buf.WriteString(strings.Join(conflict, ", ")) 42 | buf.WriteString(") DO UPDATE SET ") 43 | 44 | for i, v := range update { 45 | if i != 0 { 46 | buf.WriteByte(',') 47 | } 48 | quoted := strmangle.IdentQuote(dia.LQ, dia.RQ, v) 49 | buf.WriteString(quoted) 50 | buf.WriteString(" = EXCLUDED.") 51 | buf.WriteString(quoted) 52 | } 53 | } 54 | 55 | if len(ret) != 0 { 56 | buf.WriteString(" RETURNING ") 57 | buf.WriteString(strings.Join(ret, ", ")) 58 | } 59 | 60 | return buf.String() 61 | } 62 | -------------------------------------------------------------------------------- /backend/patientdb/models/schema_migrations_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 3.7.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package models 5 | 6 | import ( 7 | "bytes" 8 | "context" 9 | "reflect" 10 | "testing" 11 | 12 | "github.com/volatiletech/sqlboiler/boil" 13 | "github.com/volatiletech/sqlboiler/queries" 14 | "github.com/volatiletech/sqlboiler/randomize" 15 | "github.com/volatiletech/sqlboiler/strmangle" 16 | ) 17 | 18 | var ( 19 | // Relationships sometimes use the reflection helper queries.Equal/queries.Assign 20 | // so force a package dependency in case they don't. 21 | _ = queries.Equal 22 | ) 23 | 24 | func testSchemaMigrations(t *testing.T) { 25 | t.Parallel() 26 | 27 | query := SchemaMigrations() 28 | 29 | if query.Query == nil { 30 | t.Error("expected a query, got nothing") 31 | } 32 | } 33 | 34 | func testSchemaMigrationsDelete(t *testing.T) { 35 | t.Parallel() 36 | 37 | seed := randomize.NewSeed() 38 | var err error 39 | o := &SchemaMigration{} 40 | if err = randomize.Struct(seed, o, schemaMigrationDBTypes, true, schemaMigrationColumnsWithDefault...); err != nil { 41 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 42 | } 43 | 44 | ctx := context.Background() 45 | tx := MustTx(boil.BeginTx(ctx, nil)) 46 | defer func() { _ = tx.Rollback() }() 47 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 48 | t.Error(err) 49 | } 50 | 51 | if rowsAff, err := o.Delete(ctx, tx); err != nil { 52 | t.Error(err) 53 | } else if rowsAff != 1 { 54 | t.Error("should only have deleted one row, but affected:", rowsAff) 55 | } 56 | 57 | count, err := SchemaMigrations().Count(ctx, tx) 58 | if err != nil { 59 | t.Error(err) 60 | } 61 | 62 | if count != 0 { 63 | t.Error("want zero records, got:", count) 64 | } 65 | } 66 | 67 | func testSchemaMigrationsQueryDeleteAll(t *testing.T) { 68 | t.Parallel() 69 | 70 | seed := randomize.NewSeed() 71 | var err error 72 | o := &SchemaMigration{} 73 | if err = randomize.Struct(seed, o, schemaMigrationDBTypes, true, schemaMigrationColumnsWithDefault...); err != nil { 74 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 75 | } 76 | 77 | ctx := context.Background() 78 | tx := MustTx(boil.BeginTx(ctx, nil)) 79 | defer func() { _ = tx.Rollback() }() 80 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 81 | t.Error(err) 82 | } 83 | 84 | if rowsAff, err := SchemaMigrations().DeleteAll(ctx, tx); err != nil { 85 | t.Error(err) 86 | } else if rowsAff != 1 { 87 | t.Error("should only have deleted one row, but affected:", rowsAff) 88 | } 89 | 90 | count, err := SchemaMigrations().Count(ctx, tx) 91 | if err != nil { 92 | t.Error(err) 93 | } 94 | 95 | if count != 0 { 96 | t.Error("want zero records, got:", count) 97 | } 98 | } 99 | 100 | func testSchemaMigrationsSliceDeleteAll(t *testing.T) { 101 | t.Parallel() 102 | 103 | seed := randomize.NewSeed() 104 | var err error 105 | o := &SchemaMigration{} 106 | if err = randomize.Struct(seed, o, schemaMigrationDBTypes, true, schemaMigrationColumnsWithDefault...); err != nil { 107 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 108 | } 109 | 110 | ctx := context.Background() 111 | tx := MustTx(boil.BeginTx(ctx, nil)) 112 | defer func() { _ = tx.Rollback() }() 113 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 114 | t.Error(err) 115 | } 116 | 117 | slice := SchemaMigrationSlice{o} 118 | 119 | if rowsAff, err := slice.DeleteAll(ctx, tx); err != nil { 120 | t.Error(err) 121 | } else if rowsAff != 1 { 122 | t.Error("should only have deleted one row, but affected:", rowsAff) 123 | } 124 | 125 | count, err := SchemaMigrations().Count(ctx, tx) 126 | if err != nil { 127 | t.Error(err) 128 | } 129 | 130 | if count != 0 { 131 | t.Error("want zero records, got:", count) 132 | } 133 | } 134 | 135 | func testSchemaMigrationsExists(t *testing.T) { 136 | t.Parallel() 137 | 138 | seed := randomize.NewSeed() 139 | var err error 140 | o := &SchemaMigration{} 141 | if err = randomize.Struct(seed, o, schemaMigrationDBTypes, true, schemaMigrationColumnsWithDefault...); err != nil { 142 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 143 | } 144 | 145 | ctx := context.Background() 146 | tx := MustTx(boil.BeginTx(ctx, nil)) 147 | defer func() { _ = tx.Rollback() }() 148 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 149 | t.Error(err) 150 | } 151 | 152 | e, err := SchemaMigrationExists(ctx, tx, o.Version) 153 | if err != nil { 154 | t.Errorf("Unable to check if SchemaMigration exists: %s", err) 155 | } 156 | if !e { 157 | t.Errorf("Expected SchemaMigrationExists to return true, but got false.") 158 | } 159 | } 160 | 161 | func testSchemaMigrationsFind(t *testing.T) { 162 | t.Parallel() 163 | 164 | seed := randomize.NewSeed() 165 | var err error 166 | o := &SchemaMigration{} 167 | if err = randomize.Struct(seed, o, schemaMigrationDBTypes, true, schemaMigrationColumnsWithDefault...); err != nil { 168 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 169 | } 170 | 171 | ctx := context.Background() 172 | tx := MustTx(boil.BeginTx(ctx, nil)) 173 | defer func() { _ = tx.Rollback() }() 174 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 175 | t.Error(err) 176 | } 177 | 178 | schemaMigrationFound, err := FindSchemaMigration(ctx, tx, o.Version) 179 | if err != nil { 180 | t.Error(err) 181 | } 182 | 183 | if schemaMigrationFound == nil { 184 | t.Error("want a record, got nil") 185 | } 186 | } 187 | 188 | func testSchemaMigrationsBind(t *testing.T) { 189 | t.Parallel() 190 | 191 | seed := randomize.NewSeed() 192 | var err error 193 | o := &SchemaMigration{} 194 | if err = randomize.Struct(seed, o, schemaMigrationDBTypes, true, schemaMigrationColumnsWithDefault...); err != nil { 195 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 196 | } 197 | 198 | ctx := context.Background() 199 | tx := MustTx(boil.BeginTx(ctx, nil)) 200 | defer func() { _ = tx.Rollback() }() 201 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 202 | t.Error(err) 203 | } 204 | 205 | if err = SchemaMigrations().Bind(ctx, tx, o); err != nil { 206 | t.Error(err) 207 | } 208 | } 209 | 210 | func testSchemaMigrationsOne(t *testing.T) { 211 | t.Parallel() 212 | 213 | seed := randomize.NewSeed() 214 | var err error 215 | o := &SchemaMigration{} 216 | if err = randomize.Struct(seed, o, schemaMigrationDBTypes, true, schemaMigrationColumnsWithDefault...); err != nil { 217 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 218 | } 219 | 220 | ctx := context.Background() 221 | tx := MustTx(boil.BeginTx(ctx, nil)) 222 | defer func() { _ = tx.Rollback() }() 223 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 224 | t.Error(err) 225 | } 226 | 227 | if x, err := SchemaMigrations().One(ctx, tx); err != nil { 228 | t.Error(err) 229 | } else if x == nil { 230 | t.Error("expected to get a non nil record") 231 | } 232 | } 233 | 234 | func testSchemaMigrationsAll(t *testing.T) { 235 | t.Parallel() 236 | 237 | seed := randomize.NewSeed() 238 | var err error 239 | schemaMigrationOne := &SchemaMigration{} 240 | schemaMigrationTwo := &SchemaMigration{} 241 | if err = randomize.Struct(seed, schemaMigrationOne, schemaMigrationDBTypes, false, schemaMigrationColumnsWithDefault...); err != nil { 242 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 243 | } 244 | if err = randomize.Struct(seed, schemaMigrationTwo, schemaMigrationDBTypes, false, schemaMigrationColumnsWithDefault...); err != nil { 245 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 246 | } 247 | 248 | ctx := context.Background() 249 | tx := MustTx(boil.BeginTx(ctx, nil)) 250 | defer func() { _ = tx.Rollback() }() 251 | if err = schemaMigrationOne.Insert(ctx, tx, boil.Infer()); err != nil { 252 | t.Error(err) 253 | } 254 | if err = schemaMigrationTwo.Insert(ctx, tx, boil.Infer()); err != nil { 255 | t.Error(err) 256 | } 257 | 258 | slice, err := SchemaMigrations().All(ctx, tx) 259 | if err != nil { 260 | t.Error(err) 261 | } 262 | 263 | if len(slice) != 2 { 264 | t.Error("want 2 records, got:", len(slice)) 265 | } 266 | } 267 | 268 | func testSchemaMigrationsCount(t *testing.T) { 269 | t.Parallel() 270 | 271 | var err error 272 | seed := randomize.NewSeed() 273 | schemaMigrationOne := &SchemaMigration{} 274 | schemaMigrationTwo := &SchemaMigration{} 275 | if err = randomize.Struct(seed, schemaMigrationOne, schemaMigrationDBTypes, false, schemaMigrationColumnsWithDefault...); err != nil { 276 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 277 | } 278 | if err = randomize.Struct(seed, schemaMigrationTwo, schemaMigrationDBTypes, false, schemaMigrationColumnsWithDefault...); err != nil { 279 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 280 | } 281 | 282 | ctx := context.Background() 283 | tx := MustTx(boil.BeginTx(ctx, nil)) 284 | defer func() { _ = tx.Rollback() }() 285 | if err = schemaMigrationOne.Insert(ctx, tx, boil.Infer()); err != nil { 286 | t.Error(err) 287 | } 288 | if err = schemaMigrationTwo.Insert(ctx, tx, boil.Infer()); err != nil { 289 | t.Error(err) 290 | } 291 | 292 | count, err := SchemaMigrations().Count(ctx, tx) 293 | if err != nil { 294 | t.Error(err) 295 | } 296 | 297 | if count != 2 { 298 | t.Error("want 2 records, got:", count) 299 | } 300 | } 301 | 302 | func schemaMigrationBeforeInsertHook(ctx context.Context, e boil.ContextExecutor, o *SchemaMigration) error { 303 | *o = SchemaMigration{} 304 | return nil 305 | } 306 | 307 | func schemaMigrationAfterInsertHook(ctx context.Context, e boil.ContextExecutor, o *SchemaMigration) error { 308 | *o = SchemaMigration{} 309 | return nil 310 | } 311 | 312 | func schemaMigrationAfterSelectHook(ctx context.Context, e boil.ContextExecutor, o *SchemaMigration) error { 313 | *o = SchemaMigration{} 314 | return nil 315 | } 316 | 317 | func schemaMigrationBeforeUpdateHook(ctx context.Context, e boil.ContextExecutor, o *SchemaMigration) error { 318 | *o = SchemaMigration{} 319 | return nil 320 | } 321 | 322 | func schemaMigrationAfterUpdateHook(ctx context.Context, e boil.ContextExecutor, o *SchemaMigration) error { 323 | *o = SchemaMigration{} 324 | return nil 325 | } 326 | 327 | func schemaMigrationBeforeDeleteHook(ctx context.Context, e boil.ContextExecutor, o *SchemaMigration) error { 328 | *o = SchemaMigration{} 329 | return nil 330 | } 331 | 332 | func schemaMigrationAfterDeleteHook(ctx context.Context, e boil.ContextExecutor, o *SchemaMigration) error { 333 | *o = SchemaMigration{} 334 | return nil 335 | } 336 | 337 | func schemaMigrationBeforeUpsertHook(ctx context.Context, e boil.ContextExecutor, o *SchemaMigration) error { 338 | *o = SchemaMigration{} 339 | return nil 340 | } 341 | 342 | func schemaMigrationAfterUpsertHook(ctx context.Context, e boil.ContextExecutor, o *SchemaMigration) error { 343 | *o = SchemaMigration{} 344 | return nil 345 | } 346 | 347 | func testSchemaMigrationsHooks(t *testing.T) { 348 | t.Parallel() 349 | 350 | var err error 351 | 352 | ctx := context.Background() 353 | empty := &SchemaMigration{} 354 | o := &SchemaMigration{} 355 | 356 | seed := randomize.NewSeed() 357 | if err = randomize.Struct(seed, o, schemaMigrationDBTypes, false); err != nil { 358 | t.Errorf("Unable to randomize SchemaMigration object: %s", err) 359 | } 360 | 361 | AddSchemaMigrationHook(boil.BeforeInsertHook, schemaMigrationBeforeInsertHook) 362 | if err = o.doBeforeInsertHooks(ctx, nil); err != nil { 363 | t.Errorf("Unable to execute doBeforeInsertHooks: %s", err) 364 | } 365 | if !reflect.DeepEqual(o, empty) { 366 | t.Errorf("Expected BeforeInsertHook function to empty object, but got: %#v", o) 367 | } 368 | schemaMigrationBeforeInsertHooks = []SchemaMigrationHook{} 369 | 370 | AddSchemaMigrationHook(boil.AfterInsertHook, schemaMigrationAfterInsertHook) 371 | if err = o.doAfterInsertHooks(ctx, nil); err != nil { 372 | t.Errorf("Unable to execute doAfterInsertHooks: %s", err) 373 | } 374 | if !reflect.DeepEqual(o, empty) { 375 | t.Errorf("Expected AfterInsertHook function to empty object, but got: %#v", o) 376 | } 377 | schemaMigrationAfterInsertHooks = []SchemaMigrationHook{} 378 | 379 | AddSchemaMigrationHook(boil.AfterSelectHook, schemaMigrationAfterSelectHook) 380 | if err = o.doAfterSelectHooks(ctx, nil); err != nil { 381 | t.Errorf("Unable to execute doAfterSelectHooks: %s", err) 382 | } 383 | if !reflect.DeepEqual(o, empty) { 384 | t.Errorf("Expected AfterSelectHook function to empty object, but got: %#v", o) 385 | } 386 | schemaMigrationAfterSelectHooks = []SchemaMigrationHook{} 387 | 388 | AddSchemaMigrationHook(boil.BeforeUpdateHook, schemaMigrationBeforeUpdateHook) 389 | if err = o.doBeforeUpdateHooks(ctx, nil); err != nil { 390 | t.Errorf("Unable to execute doBeforeUpdateHooks: %s", err) 391 | } 392 | if !reflect.DeepEqual(o, empty) { 393 | t.Errorf("Expected BeforeUpdateHook function to empty object, but got: %#v", o) 394 | } 395 | schemaMigrationBeforeUpdateHooks = []SchemaMigrationHook{} 396 | 397 | AddSchemaMigrationHook(boil.AfterUpdateHook, schemaMigrationAfterUpdateHook) 398 | if err = o.doAfterUpdateHooks(ctx, nil); err != nil { 399 | t.Errorf("Unable to execute doAfterUpdateHooks: %s", err) 400 | } 401 | if !reflect.DeepEqual(o, empty) { 402 | t.Errorf("Expected AfterUpdateHook function to empty object, but got: %#v", o) 403 | } 404 | schemaMigrationAfterUpdateHooks = []SchemaMigrationHook{} 405 | 406 | AddSchemaMigrationHook(boil.BeforeDeleteHook, schemaMigrationBeforeDeleteHook) 407 | if err = o.doBeforeDeleteHooks(ctx, nil); err != nil { 408 | t.Errorf("Unable to execute doBeforeDeleteHooks: %s", err) 409 | } 410 | if !reflect.DeepEqual(o, empty) { 411 | t.Errorf("Expected BeforeDeleteHook function to empty object, but got: %#v", o) 412 | } 413 | schemaMigrationBeforeDeleteHooks = []SchemaMigrationHook{} 414 | 415 | AddSchemaMigrationHook(boil.AfterDeleteHook, schemaMigrationAfterDeleteHook) 416 | if err = o.doAfterDeleteHooks(ctx, nil); err != nil { 417 | t.Errorf("Unable to execute doAfterDeleteHooks: %s", err) 418 | } 419 | if !reflect.DeepEqual(o, empty) { 420 | t.Errorf("Expected AfterDeleteHook function to empty object, but got: %#v", o) 421 | } 422 | schemaMigrationAfterDeleteHooks = []SchemaMigrationHook{} 423 | 424 | AddSchemaMigrationHook(boil.BeforeUpsertHook, schemaMigrationBeforeUpsertHook) 425 | if err = o.doBeforeUpsertHooks(ctx, nil); err != nil { 426 | t.Errorf("Unable to execute doBeforeUpsertHooks: %s", err) 427 | } 428 | if !reflect.DeepEqual(o, empty) { 429 | t.Errorf("Expected BeforeUpsertHook function to empty object, but got: %#v", o) 430 | } 431 | schemaMigrationBeforeUpsertHooks = []SchemaMigrationHook{} 432 | 433 | AddSchemaMigrationHook(boil.AfterUpsertHook, schemaMigrationAfterUpsertHook) 434 | if err = o.doAfterUpsertHooks(ctx, nil); err != nil { 435 | t.Errorf("Unable to execute doAfterUpsertHooks: %s", err) 436 | } 437 | if !reflect.DeepEqual(o, empty) { 438 | t.Errorf("Expected AfterUpsertHook function to empty object, but got: %#v", o) 439 | } 440 | schemaMigrationAfterUpsertHooks = []SchemaMigrationHook{} 441 | } 442 | 443 | func testSchemaMigrationsInsert(t *testing.T) { 444 | t.Parallel() 445 | 446 | seed := randomize.NewSeed() 447 | var err error 448 | o := &SchemaMigration{} 449 | if err = randomize.Struct(seed, o, schemaMigrationDBTypes, true, schemaMigrationColumnsWithDefault...); err != nil { 450 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 451 | } 452 | 453 | ctx := context.Background() 454 | tx := MustTx(boil.BeginTx(ctx, nil)) 455 | defer func() { _ = tx.Rollback() }() 456 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 457 | t.Error(err) 458 | } 459 | 460 | count, err := SchemaMigrations().Count(ctx, tx) 461 | if err != nil { 462 | t.Error(err) 463 | } 464 | 465 | if count != 1 { 466 | t.Error("want one record, got:", count) 467 | } 468 | } 469 | 470 | func testSchemaMigrationsInsertWhitelist(t *testing.T) { 471 | t.Parallel() 472 | 473 | seed := randomize.NewSeed() 474 | var err error 475 | o := &SchemaMigration{} 476 | if err = randomize.Struct(seed, o, schemaMigrationDBTypes, true); err != nil { 477 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 478 | } 479 | 480 | ctx := context.Background() 481 | tx := MustTx(boil.BeginTx(ctx, nil)) 482 | defer func() { _ = tx.Rollback() }() 483 | if err = o.Insert(ctx, tx, boil.Whitelist(schemaMigrationColumnsWithoutDefault...)); err != nil { 484 | t.Error(err) 485 | } 486 | 487 | count, err := SchemaMigrations().Count(ctx, tx) 488 | if err != nil { 489 | t.Error(err) 490 | } 491 | 492 | if count != 1 { 493 | t.Error("want one record, got:", count) 494 | } 495 | } 496 | 497 | func testSchemaMigrationsReload(t *testing.T) { 498 | t.Parallel() 499 | 500 | seed := randomize.NewSeed() 501 | var err error 502 | o := &SchemaMigration{} 503 | if err = randomize.Struct(seed, o, schemaMigrationDBTypes, true, schemaMigrationColumnsWithDefault...); err != nil { 504 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 505 | } 506 | 507 | ctx := context.Background() 508 | tx := MustTx(boil.BeginTx(ctx, nil)) 509 | defer func() { _ = tx.Rollback() }() 510 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 511 | t.Error(err) 512 | } 513 | 514 | if err = o.Reload(ctx, tx); err != nil { 515 | t.Error(err) 516 | } 517 | } 518 | 519 | func testSchemaMigrationsReloadAll(t *testing.T) { 520 | t.Parallel() 521 | 522 | seed := randomize.NewSeed() 523 | var err error 524 | o := &SchemaMigration{} 525 | if err = randomize.Struct(seed, o, schemaMigrationDBTypes, true, schemaMigrationColumnsWithDefault...); err != nil { 526 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 527 | } 528 | 529 | ctx := context.Background() 530 | tx := MustTx(boil.BeginTx(ctx, nil)) 531 | defer func() { _ = tx.Rollback() }() 532 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 533 | t.Error(err) 534 | } 535 | 536 | slice := SchemaMigrationSlice{o} 537 | 538 | if err = slice.ReloadAll(ctx, tx); err != nil { 539 | t.Error(err) 540 | } 541 | } 542 | 543 | func testSchemaMigrationsSelect(t *testing.T) { 544 | t.Parallel() 545 | 546 | seed := randomize.NewSeed() 547 | var err error 548 | o := &SchemaMigration{} 549 | if err = randomize.Struct(seed, o, schemaMigrationDBTypes, true, schemaMigrationColumnsWithDefault...); err != nil { 550 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 551 | } 552 | 553 | ctx := context.Background() 554 | tx := MustTx(boil.BeginTx(ctx, nil)) 555 | defer func() { _ = tx.Rollback() }() 556 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 557 | t.Error(err) 558 | } 559 | 560 | slice, err := SchemaMigrations().All(ctx, tx) 561 | if err != nil { 562 | t.Error(err) 563 | } 564 | 565 | if len(slice) != 1 { 566 | t.Error("want one record, got:", len(slice)) 567 | } 568 | } 569 | 570 | var ( 571 | schemaMigrationDBTypes = map[string]string{`Version`: `bigint`, `Dirty`: `boolean`} 572 | _ = bytes.MinRead 573 | ) 574 | 575 | func testSchemaMigrationsUpdate(t *testing.T) { 576 | t.Parallel() 577 | 578 | if 0 == len(schemaMigrationPrimaryKeyColumns) { 579 | t.Skip("Skipping table with no primary key columns") 580 | } 581 | if len(schemaMigrationAllColumns) == len(schemaMigrationPrimaryKeyColumns) { 582 | t.Skip("Skipping table with only primary key columns") 583 | } 584 | 585 | seed := randomize.NewSeed() 586 | var err error 587 | o := &SchemaMigration{} 588 | if err = randomize.Struct(seed, o, schemaMigrationDBTypes, true, schemaMigrationColumnsWithDefault...); err != nil { 589 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 590 | } 591 | 592 | ctx := context.Background() 593 | tx := MustTx(boil.BeginTx(ctx, nil)) 594 | defer func() { _ = tx.Rollback() }() 595 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 596 | t.Error(err) 597 | } 598 | 599 | count, err := SchemaMigrations().Count(ctx, tx) 600 | if err != nil { 601 | t.Error(err) 602 | } 603 | 604 | if count != 1 { 605 | t.Error("want one record, got:", count) 606 | } 607 | 608 | if err = randomize.Struct(seed, o, schemaMigrationDBTypes, true, schemaMigrationPrimaryKeyColumns...); err != nil { 609 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 610 | } 611 | 612 | if rowsAff, err := o.Update(ctx, tx, boil.Infer()); err != nil { 613 | t.Error(err) 614 | } else if rowsAff != 1 { 615 | t.Error("should only affect one row but affected", rowsAff) 616 | } 617 | } 618 | 619 | func testSchemaMigrationsSliceUpdateAll(t *testing.T) { 620 | t.Parallel() 621 | 622 | if len(schemaMigrationAllColumns) == len(schemaMigrationPrimaryKeyColumns) { 623 | t.Skip("Skipping table with only primary key columns") 624 | } 625 | 626 | seed := randomize.NewSeed() 627 | var err error 628 | o := &SchemaMigration{} 629 | if err = randomize.Struct(seed, o, schemaMigrationDBTypes, true, schemaMigrationColumnsWithDefault...); err != nil { 630 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 631 | } 632 | 633 | ctx := context.Background() 634 | tx := MustTx(boil.BeginTx(ctx, nil)) 635 | defer func() { _ = tx.Rollback() }() 636 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 637 | t.Error(err) 638 | } 639 | 640 | count, err := SchemaMigrations().Count(ctx, tx) 641 | if err != nil { 642 | t.Error(err) 643 | } 644 | 645 | if count != 1 { 646 | t.Error("want one record, got:", count) 647 | } 648 | 649 | if err = randomize.Struct(seed, o, schemaMigrationDBTypes, true, schemaMigrationPrimaryKeyColumns...); err != nil { 650 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 651 | } 652 | 653 | // Remove Primary keys and unique columns from what we plan to update 654 | var fields []string 655 | if strmangle.StringSliceMatch(schemaMigrationAllColumns, schemaMigrationPrimaryKeyColumns) { 656 | fields = schemaMigrationAllColumns 657 | } else { 658 | fields = strmangle.SetComplement( 659 | schemaMigrationAllColumns, 660 | schemaMigrationPrimaryKeyColumns, 661 | ) 662 | } 663 | 664 | value := reflect.Indirect(reflect.ValueOf(o)) 665 | typ := reflect.TypeOf(o).Elem() 666 | n := typ.NumField() 667 | 668 | updateMap := M{} 669 | for _, col := range fields { 670 | for i := 0; i < n; i++ { 671 | f := typ.Field(i) 672 | if f.Tag.Get("boil") == col { 673 | updateMap[col] = value.Field(i).Interface() 674 | } 675 | } 676 | } 677 | 678 | slice := SchemaMigrationSlice{o} 679 | if rowsAff, err := slice.UpdateAll(ctx, tx, updateMap); err != nil { 680 | t.Error(err) 681 | } else if rowsAff != 1 { 682 | t.Error("wanted one record updated but got", rowsAff) 683 | } 684 | } 685 | 686 | func testSchemaMigrationsUpsert(t *testing.T) { 687 | t.Parallel() 688 | 689 | if len(schemaMigrationAllColumns) == len(schemaMigrationPrimaryKeyColumns) { 690 | t.Skip("Skipping table with only primary key columns") 691 | } 692 | 693 | seed := randomize.NewSeed() 694 | var err error 695 | // Attempt the INSERT side of an UPSERT 696 | o := SchemaMigration{} 697 | if err = randomize.Struct(seed, &o, schemaMigrationDBTypes, true); err != nil { 698 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 699 | } 700 | 701 | ctx := context.Background() 702 | tx := MustTx(boil.BeginTx(ctx, nil)) 703 | defer func() { _ = tx.Rollback() }() 704 | if err = o.Upsert(ctx, tx, false, nil, boil.Infer(), boil.Infer()); err != nil { 705 | t.Errorf("Unable to upsert SchemaMigration: %s", err) 706 | } 707 | 708 | count, err := SchemaMigrations().Count(ctx, tx) 709 | if err != nil { 710 | t.Error(err) 711 | } 712 | if count != 1 { 713 | t.Error("want one record, got:", count) 714 | } 715 | 716 | // Attempt the UPDATE side of an UPSERT 717 | if err = randomize.Struct(seed, &o, schemaMigrationDBTypes, false, schemaMigrationPrimaryKeyColumns...); err != nil { 718 | t.Errorf("Unable to randomize SchemaMigration struct: %s", err) 719 | } 720 | 721 | if err = o.Upsert(ctx, tx, true, nil, boil.Infer(), boil.Infer()); err != nil { 722 | t.Errorf("Unable to upsert SchemaMigration: %s", err) 723 | } 724 | 725 | count, err = SchemaMigrations().Count(ctx, tx) 726 | if err != nil { 727 | t.Error(err) 728 | } 729 | if count != 1 { 730 | t.Error("want one record, got:", count) 731 | } 732 | } 733 | -------------------------------------------------------------------------------- /backend/patientdb/models/user_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by SQLBoiler 3.7.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. 2 | // This file is meant to be re-generated in place and/or deleted at any time. 3 | 4 | package models 5 | 6 | import ( 7 | "bytes" 8 | "context" 9 | "reflect" 10 | "testing" 11 | 12 | "github.com/volatiletech/sqlboiler/boil" 13 | "github.com/volatiletech/sqlboiler/queries" 14 | "github.com/volatiletech/sqlboiler/randomize" 15 | "github.com/volatiletech/sqlboiler/strmangle" 16 | ) 17 | 18 | var ( 19 | // Relationships sometimes use the reflection helper queries.Equal/queries.Assign 20 | // so force a package dependency in case they don't. 21 | _ = queries.Equal 22 | ) 23 | 24 | func testUsers(t *testing.T) { 25 | t.Parallel() 26 | 27 | query := Users() 28 | 29 | if query.Query == nil { 30 | t.Error("expected a query, got nothing") 31 | } 32 | } 33 | 34 | func testUsersDelete(t *testing.T) { 35 | t.Parallel() 36 | 37 | seed := randomize.NewSeed() 38 | var err error 39 | o := &User{} 40 | if err = randomize.Struct(seed, o, userDBTypes, true, userColumnsWithDefault...); err != nil { 41 | t.Errorf("Unable to randomize User struct: %s", err) 42 | } 43 | 44 | ctx := context.Background() 45 | tx := MustTx(boil.BeginTx(ctx, nil)) 46 | defer func() { _ = tx.Rollback() }() 47 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 48 | t.Error(err) 49 | } 50 | 51 | if rowsAff, err := o.Delete(ctx, tx); err != nil { 52 | t.Error(err) 53 | } else if rowsAff != 1 { 54 | t.Error("should only have deleted one row, but affected:", rowsAff) 55 | } 56 | 57 | count, err := Users().Count(ctx, tx) 58 | if err != nil { 59 | t.Error(err) 60 | } 61 | 62 | if count != 0 { 63 | t.Error("want zero records, got:", count) 64 | } 65 | } 66 | 67 | func testUsersQueryDeleteAll(t *testing.T) { 68 | t.Parallel() 69 | 70 | seed := randomize.NewSeed() 71 | var err error 72 | o := &User{} 73 | if err = randomize.Struct(seed, o, userDBTypes, true, userColumnsWithDefault...); err != nil { 74 | t.Errorf("Unable to randomize User struct: %s", err) 75 | } 76 | 77 | ctx := context.Background() 78 | tx := MustTx(boil.BeginTx(ctx, nil)) 79 | defer func() { _ = tx.Rollback() }() 80 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 81 | t.Error(err) 82 | } 83 | 84 | if rowsAff, err := Users().DeleteAll(ctx, tx); err != nil { 85 | t.Error(err) 86 | } else if rowsAff != 1 { 87 | t.Error("should only have deleted one row, but affected:", rowsAff) 88 | } 89 | 90 | count, err := Users().Count(ctx, tx) 91 | if err != nil { 92 | t.Error(err) 93 | } 94 | 95 | if count != 0 { 96 | t.Error("want zero records, got:", count) 97 | } 98 | } 99 | 100 | func testUsersSliceDeleteAll(t *testing.T) { 101 | t.Parallel() 102 | 103 | seed := randomize.NewSeed() 104 | var err error 105 | o := &User{} 106 | if err = randomize.Struct(seed, o, userDBTypes, true, userColumnsWithDefault...); err != nil { 107 | t.Errorf("Unable to randomize User struct: %s", err) 108 | } 109 | 110 | ctx := context.Background() 111 | tx := MustTx(boil.BeginTx(ctx, nil)) 112 | defer func() { _ = tx.Rollback() }() 113 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 114 | t.Error(err) 115 | } 116 | 117 | slice := UserSlice{o} 118 | 119 | if rowsAff, err := slice.DeleteAll(ctx, tx); err != nil { 120 | t.Error(err) 121 | } else if rowsAff != 1 { 122 | t.Error("should only have deleted one row, but affected:", rowsAff) 123 | } 124 | 125 | count, err := Users().Count(ctx, tx) 126 | if err != nil { 127 | t.Error(err) 128 | } 129 | 130 | if count != 0 { 131 | t.Error("want zero records, got:", count) 132 | } 133 | } 134 | 135 | func testUsersExists(t *testing.T) { 136 | t.Parallel() 137 | 138 | seed := randomize.NewSeed() 139 | var err error 140 | o := &User{} 141 | if err = randomize.Struct(seed, o, userDBTypes, true, userColumnsWithDefault...); err != nil { 142 | t.Errorf("Unable to randomize User struct: %s", err) 143 | } 144 | 145 | ctx := context.Background() 146 | tx := MustTx(boil.BeginTx(ctx, nil)) 147 | defer func() { _ = tx.Rollback() }() 148 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 149 | t.Error(err) 150 | } 151 | 152 | e, err := UserExists(ctx, tx, o.Userid) 153 | if err != nil { 154 | t.Errorf("Unable to check if User exists: %s", err) 155 | } 156 | if !e { 157 | t.Errorf("Expected UserExists to return true, but got false.") 158 | } 159 | } 160 | 161 | func testUsersFind(t *testing.T) { 162 | t.Parallel() 163 | 164 | seed := randomize.NewSeed() 165 | var err error 166 | o := &User{} 167 | if err = randomize.Struct(seed, o, userDBTypes, true, userColumnsWithDefault...); err != nil { 168 | t.Errorf("Unable to randomize User struct: %s", err) 169 | } 170 | 171 | ctx := context.Background() 172 | tx := MustTx(boil.BeginTx(ctx, nil)) 173 | defer func() { _ = tx.Rollback() }() 174 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 175 | t.Error(err) 176 | } 177 | 178 | userFound, err := FindUser(ctx, tx, o.Userid) 179 | if err != nil { 180 | t.Error(err) 181 | } 182 | 183 | if userFound == nil { 184 | t.Error("want a record, got nil") 185 | } 186 | } 187 | 188 | func testUsersBind(t *testing.T) { 189 | t.Parallel() 190 | 191 | seed := randomize.NewSeed() 192 | var err error 193 | o := &User{} 194 | if err = randomize.Struct(seed, o, userDBTypes, true, userColumnsWithDefault...); err != nil { 195 | t.Errorf("Unable to randomize User struct: %s", err) 196 | } 197 | 198 | ctx := context.Background() 199 | tx := MustTx(boil.BeginTx(ctx, nil)) 200 | defer func() { _ = tx.Rollback() }() 201 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 202 | t.Error(err) 203 | } 204 | 205 | if err = Users().Bind(ctx, tx, o); err != nil { 206 | t.Error(err) 207 | } 208 | } 209 | 210 | func testUsersOne(t *testing.T) { 211 | t.Parallel() 212 | 213 | seed := randomize.NewSeed() 214 | var err error 215 | o := &User{} 216 | if err = randomize.Struct(seed, o, userDBTypes, true, userColumnsWithDefault...); err != nil { 217 | t.Errorf("Unable to randomize User struct: %s", err) 218 | } 219 | 220 | ctx := context.Background() 221 | tx := MustTx(boil.BeginTx(ctx, nil)) 222 | defer func() { _ = tx.Rollback() }() 223 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 224 | t.Error(err) 225 | } 226 | 227 | if x, err := Users().One(ctx, tx); err != nil { 228 | t.Error(err) 229 | } else if x == nil { 230 | t.Error("expected to get a non nil record") 231 | } 232 | } 233 | 234 | func testUsersAll(t *testing.T) { 235 | t.Parallel() 236 | 237 | seed := randomize.NewSeed() 238 | var err error 239 | userOne := &User{} 240 | userTwo := &User{} 241 | if err = randomize.Struct(seed, userOne, userDBTypes, false, userColumnsWithDefault...); err != nil { 242 | t.Errorf("Unable to randomize User struct: %s", err) 243 | } 244 | if err = randomize.Struct(seed, userTwo, userDBTypes, false, userColumnsWithDefault...); err != nil { 245 | t.Errorf("Unable to randomize User struct: %s", err) 246 | } 247 | 248 | ctx := context.Background() 249 | tx := MustTx(boil.BeginTx(ctx, nil)) 250 | defer func() { _ = tx.Rollback() }() 251 | if err = userOne.Insert(ctx, tx, boil.Infer()); err != nil { 252 | t.Error(err) 253 | } 254 | if err = userTwo.Insert(ctx, tx, boil.Infer()); err != nil { 255 | t.Error(err) 256 | } 257 | 258 | slice, err := Users().All(ctx, tx) 259 | if err != nil { 260 | t.Error(err) 261 | } 262 | 263 | if len(slice) != 2 { 264 | t.Error("want 2 records, got:", len(slice)) 265 | } 266 | } 267 | 268 | func testUsersCount(t *testing.T) { 269 | t.Parallel() 270 | 271 | var err error 272 | seed := randomize.NewSeed() 273 | userOne := &User{} 274 | userTwo := &User{} 275 | if err = randomize.Struct(seed, userOne, userDBTypes, false, userColumnsWithDefault...); err != nil { 276 | t.Errorf("Unable to randomize User struct: %s", err) 277 | } 278 | if err = randomize.Struct(seed, userTwo, userDBTypes, false, userColumnsWithDefault...); err != nil { 279 | t.Errorf("Unable to randomize User struct: %s", err) 280 | } 281 | 282 | ctx := context.Background() 283 | tx := MustTx(boil.BeginTx(ctx, nil)) 284 | defer func() { _ = tx.Rollback() }() 285 | if err = userOne.Insert(ctx, tx, boil.Infer()); err != nil { 286 | t.Error(err) 287 | } 288 | if err = userTwo.Insert(ctx, tx, boil.Infer()); err != nil { 289 | t.Error(err) 290 | } 291 | 292 | count, err := Users().Count(ctx, tx) 293 | if err != nil { 294 | t.Error(err) 295 | } 296 | 297 | if count != 2 { 298 | t.Error("want 2 records, got:", count) 299 | } 300 | } 301 | 302 | func userBeforeInsertHook(ctx context.Context, e boil.ContextExecutor, o *User) error { 303 | *o = User{} 304 | return nil 305 | } 306 | 307 | func userAfterInsertHook(ctx context.Context, e boil.ContextExecutor, o *User) error { 308 | *o = User{} 309 | return nil 310 | } 311 | 312 | func userAfterSelectHook(ctx context.Context, e boil.ContextExecutor, o *User) error { 313 | *o = User{} 314 | return nil 315 | } 316 | 317 | func userBeforeUpdateHook(ctx context.Context, e boil.ContextExecutor, o *User) error { 318 | *o = User{} 319 | return nil 320 | } 321 | 322 | func userAfterUpdateHook(ctx context.Context, e boil.ContextExecutor, o *User) error { 323 | *o = User{} 324 | return nil 325 | } 326 | 327 | func userBeforeDeleteHook(ctx context.Context, e boil.ContextExecutor, o *User) error { 328 | *o = User{} 329 | return nil 330 | } 331 | 332 | func userAfterDeleteHook(ctx context.Context, e boil.ContextExecutor, o *User) error { 333 | *o = User{} 334 | return nil 335 | } 336 | 337 | func userBeforeUpsertHook(ctx context.Context, e boil.ContextExecutor, o *User) error { 338 | *o = User{} 339 | return nil 340 | } 341 | 342 | func userAfterUpsertHook(ctx context.Context, e boil.ContextExecutor, o *User) error { 343 | *o = User{} 344 | return nil 345 | } 346 | 347 | func testUsersHooks(t *testing.T) { 348 | t.Parallel() 349 | 350 | var err error 351 | 352 | ctx := context.Background() 353 | empty := &User{} 354 | o := &User{} 355 | 356 | seed := randomize.NewSeed() 357 | if err = randomize.Struct(seed, o, userDBTypes, false); err != nil { 358 | t.Errorf("Unable to randomize User object: %s", err) 359 | } 360 | 361 | AddUserHook(boil.BeforeInsertHook, userBeforeInsertHook) 362 | if err = o.doBeforeInsertHooks(ctx, nil); err != nil { 363 | t.Errorf("Unable to execute doBeforeInsertHooks: %s", err) 364 | } 365 | if !reflect.DeepEqual(o, empty) { 366 | t.Errorf("Expected BeforeInsertHook function to empty object, but got: %#v", o) 367 | } 368 | userBeforeInsertHooks = []UserHook{} 369 | 370 | AddUserHook(boil.AfterInsertHook, userAfterInsertHook) 371 | if err = o.doAfterInsertHooks(ctx, nil); err != nil { 372 | t.Errorf("Unable to execute doAfterInsertHooks: %s", err) 373 | } 374 | if !reflect.DeepEqual(o, empty) { 375 | t.Errorf("Expected AfterInsertHook function to empty object, but got: %#v", o) 376 | } 377 | userAfterInsertHooks = []UserHook{} 378 | 379 | AddUserHook(boil.AfterSelectHook, userAfterSelectHook) 380 | if err = o.doAfterSelectHooks(ctx, nil); err != nil { 381 | t.Errorf("Unable to execute doAfterSelectHooks: %s", err) 382 | } 383 | if !reflect.DeepEqual(o, empty) { 384 | t.Errorf("Expected AfterSelectHook function to empty object, but got: %#v", o) 385 | } 386 | userAfterSelectHooks = []UserHook{} 387 | 388 | AddUserHook(boil.BeforeUpdateHook, userBeforeUpdateHook) 389 | if err = o.doBeforeUpdateHooks(ctx, nil); err != nil { 390 | t.Errorf("Unable to execute doBeforeUpdateHooks: %s", err) 391 | } 392 | if !reflect.DeepEqual(o, empty) { 393 | t.Errorf("Expected BeforeUpdateHook function to empty object, but got: %#v", o) 394 | } 395 | userBeforeUpdateHooks = []UserHook{} 396 | 397 | AddUserHook(boil.AfterUpdateHook, userAfterUpdateHook) 398 | if err = o.doAfterUpdateHooks(ctx, nil); err != nil { 399 | t.Errorf("Unable to execute doAfterUpdateHooks: %s", err) 400 | } 401 | if !reflect.DeepEqual(o, empty) { 402 | t.Errorf("Expected AfterUpdateHook function to empty object, but got: %#v", o) 403 | } 404 | userAfterUpdateHooks = []UserHook{} 405 | 406 | AddUserHook(boil.BeforeDeleteHook, userBeforeDeleteHook) 407 | if err = o.doBeforeDeleteHooks(ctx, nil); err != nil { 408 | t.Errorf("Unable to execute doBeforeDeleteHooks: %s", err) 409 | } 410 | if !reflect.DeepEqual(o, empty) { 411 | t.Errorf("Expected BeforeDeleteHook function to empty object, but got: %#v", o) 412 | } 413 | userBeforeDeleteHooks = []UserHook{} 414 | 415 | AddUserHook(boil.AfterDeleteHook, userAfterDeleteHook) 416 | if err = o.doAfterDeleteHooks(ctx, nil); err != nil { 417 | t.Errorf("Unable to execute doAfterDeleteHooks: %s", err) 418 | } 419 | if !reflect.DeepEqual(o, empty) { 420 | t.Errorf("Expected AfterDeleteHook function to empty object, but got: %#v", o) 421 | } 422 | userAfterDeleteHooks = []UserHook{} 423 | 424 | AddUserHook(boil.BeforeUpsertHook, userBeforeUpsertHook) 425 | if err = o.doBeforeUpsertHooks(ctx, nil); err != nil { 426 | t.Errorf("Unable to execute doBeforeUpsertHooks: %s", err) 427 | } 428 | if !reflect.DeepEqual(o, empty) { 429 | t.Errorf("Expected BeforeUpsertHook function to empty object, but got: %#v", o) 430 | } 431 | userBeforeUpsertHooks = []UserHook{} 432 | 433 | AddUserHook(boil.AfterUpsertHook, userAfterUpsertHook) 434 | if err = o.doAfterUpsertHooks(ctx, nil); err != nil { 435 | t.Errorf("Unable to execute doAfterUpsertHooks: %s", err) 436 | } 437 | if !reflect.DeepEqual(o, empty) { 438 | t.Errorf("Expected AfterUpsertHook function to empty object, but got: %#v", o) 439 | } 440 | userAfterUpsertHooks = []UserHook{} 441 | } 442 | 443 | func testUsersInsert(t *testing.T) { 444 | t.Parallel() 445 | 446 | seed := randomize.NewSeed() 447 | var err error 448 | o := &User{} 449 | if err = randomize.Struct(seed, o, userDBTypes, true, userColumnsWithDefault...); err != nil { 450 | t.Errorf("Unable to randomize User struct: %s", err) 451 | } 452 | 453 | ctx := context.Background() 454 | tx := MustTx(boil.BeginTx(ctx, nil)) 455 | defer func() { _ = tx.Rollback() }() 456 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 457 | t.Error(err) 458 | } 459 | 460 | count, err := Users().Count(ctx, tx) 461 | if err != nil { 462 | t.Error(err) 463 | } 464 | 465 | if count != 1 { 466 | t.Error("want one record, got:", count) 467 | } 468 | } 469 | 470 | func testUsersInsertWhitelist(t *testing.T) { 471 | t.Parallel() 472 | 473 | seed := randomize.NewSeed() 474 | var err error 475 | o := &User{} 476 | if err = randomize.Struct(seed, o, userDBTypes, true); err != nil { 477 | t.Errorf("Unable to randomize User struct: %s", err) 478 | } 479 | 480 | ctx := context.Background() 481 | tx := MustTx(boil.BeginTx(ctx, nil)) 482 | defer func() { _ = tx.Rollback() }() 483 | if err = o.Insert(ctx, tx, boil.Whitelist(userColumnsWithoutDefault...)); err != nil { 484 | t.Error(err) 485 | } 486 | 487 | count, err := Users().Count(ctx, tx) 488 | if err != nil { 489 | t.Error(err) 490 | } 491 | 492 | if count != 1 { 493 | t.Error("want one record, got:", count) 494 | } 495 | } 496 | 497 | func testUserToManyUseridPatientNotes(t *testing.T) { 498 | var err error 499 | ctx := context.Background() 500 | tx := MustTx(boil.BeginTx(ctx, nil)) 501 | defer func() { _ = tx.Rollback() }() 502 | 503 | var a User 504 | var b, c PatientNote 505 | 506 | seed := randomize.NewSeed() 507 | if err = randomize.Struct(seed, &a, userDBTypes, true, userColumnsWithDefault...); err != nil { 508 | t.Errorf("Unable to randomize User struct: %s", err) 509 | } 510 | 511 | if err := a.Insert(ctx, tx, boil.Infer()); err != nil { 512 | t.Fatal(err) 513 | } 514 | 515 | if err = randomize.Struct(seed, &b, patientNoteDBTypes, false, patientNoteColumnsWithDefault...); err != nil { 516 | t.Fatal(err) 517 | } 518 | if err = randomize.Struct(seed, &c, patientNoteDBTypes, false, patientNoteColumnsWithDefault...); err != nil { 519 | t.Fatal(err) 520 | } 521 | 522 | b.User_Id = a.Userid 523 | c.User_Id = a.Userid 524 | 525 | if err = b.Insert(ctx, tx, boil.Infer()); err != nil { 526 | t.Fatal(err) 527 | } 528 | if err = c.Insert(ctx, tx, boil.Infer()); err != nil { 529 | t.Fatal(err) 530 | } 531 | 532 | check, err := a.UseridPatientNotes().All(ctx, tx) 533 | if err != nil { 534 | t.Fatal(err) 535 | } 536 | 537 | bFound, cFound := false, false 538 | for _, v := range check { 539 | if v.User_Id == b.User_Id { 540 | bFound = true 541 | } 542 | if v.User_Id == c.User_Id { 543 | cFound = true 544 | } 545 | } 546 | 547 | if !bFound { 548 | t.Error("expected to find b") 549 | } 550 | if !cFound { 551 | t.Error("expected to find c") 552 | } 553 | 554 | slice := UserSlice{&a} 555 | if err = a.L.LoadUseridPatientNotes(ctx, tx, false, (*[]*User)(&slice), nil); err != nil { 556 | t.Fatal(err) 557 | } 558 | if got := len(a.R.UseridPatientNotes); got != 2 { 559 | t.Error("number of eager loaded records wrong, got:", got) 560 | } 561 | 562 | a.R.UseridPatientNotes = nil 563 | if err = a.L.LoadUseridPatientNotes(ctx, tx, true, &a, nil); err != nil { 564 | t.Fatal(err) 565 | } 566 | if got := len(a.R.UseridPatientNotes); got != 2 { 567 | t.Error("number of eager loaded records wrong, got:", got) 568 | } 569 | 570 | if t.Failed() { 571 | t.Logf("%#v", check) 572 | } 573 | } 574 | 575 | func testUserToManyAddOpUseridPatientNotes(t *testing.T) { 576 | var err error 577 | 578 | ctx := context.Background() 579 | tx := MustTx(boil.BeginTx(ctx, nil)) 580 | defer func() { _ = tx.Rollback() }() 581 | 582 | var a User 583 | var b, c, d, e PatientNote 584 | 585 | seed := randomize.NewSeed() 586 | if err = randomize.Struct(seed, &a, userDBTypes, false, strmangle.SetComplement(userPrimaryKeyColumns, userColumnsWithoutDefault)...); err != nil { 587 | t.Fatal(err) 588 | } 589 | foreigners := []*PatientNote{&b, &c, &d, &e} 590 | for _, x := range foreigners { 591 | if err = randomize.Struct(seed, x, patientNoteDBTypes, false, strmangle.SetComplement(patientNotePrimaryKeyColumns, patientNoteColumnsWithoutDefault)...); err != nil { 592 | t.Fatal(err) 593 | } 594 | } 595 | 596 | if err := a.Insert(ctx, tx, boil.Infer()); err != nil { 597 | t.Fatal(err) 598 | } 599 | if err = b.Insert(ctx, tx, boil.Infer()); err != nil { 600 | t.Fatal(err) 601 | } 602 | if err = c.Insert(ctx, tx, boil.Infer()); err != nil { 603 | t.Fatal(err) 604 | } 605 | 606 | foreignersSplitByInsertion := [][]*PatientNote{ 607 | {&b, &c}, 608 | {&d, &e}, 609 | } 610 | 611 | for i, x := range foreignersSplitByInsertion { 612 | err = a.AddUseridPatientNotes(ctx, tx, i != 0, x...) 613 | if err != nil { 614 | t.Fatal(err) 615 | } 616 | 617 | first := x[0] 618 | second := x[1] 619 | 620 | if a.Userid != first.User_Id { 621 | t.Error("foreign key was wrong value", a.Userid, first.User_Id) 622 | } 623 | if a.Userid != second.User_Id { 624 | t.Error("foreign key was wrong value", a.Userid, second.User_Id) 625 | } 626 | 627 | if first.R.Userid != &a { 628 | t.Error("relationship was not added properly to the foreign slice") 629 | } 630 | if second.R.Userid != &a { 631 | t.Error("relationship was not added properly to the foreign slice") 632 | } 633 | 634 | if a.R.UseridPatientNotes[i*2] != first { 635 | t.Error("relationship struct slice not set to correct value") 636 | } 637 | if a.R.UseridPatientNotes[i*2+1] != second { 638 | t.Error("relationship struct slice not set to correct value") 639 | } 640 | 641 | count, err := a.UseridPatientNotes().Count(ctx, tx) 642 | if err != nil { 643 | t.Fatal(err) 644 | } 645 | if want := int64((i + 1) * 2); count != want { 646 | t.Error("want", want, "got", count) 647 | } 648 | } 649 | } 650 | 651 | func testUsersReload(t *testing.T) { 652 | t.Parallel() 653 | 654 | seed := randomize.NewSeed() 655 | var err error 656 | o := &User{} 657 | if err = randomize.Struct(seed, o, userDBTypes, true, userColumnsWithDefault...); err != nil { 658 | t.Errorf("Unable to randomize User struct: %s", err) 659 | } 660 | 661 | ctx := context.Background() 662 | tx := MustTx(boil.BeginTx(ctx, nil)) 663 | defer func() { _ = tx.Rollback() }() 664 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 665 | t.Error(err) 666 | } 667 | 668 | if err = o.Reload(ctx, tx); err != nil { 669 | t.Error(err) 670 | } 671 | } 672 | 673 | func testUsersReloadAll(t *testing.T) { 674 | t.Parallel() 675 | 676 | seed := randomize.NewSeed() 677 | var err error 678 | o := &User{} 679 | if err = randomize.Struct(seed, o, userDBTypes, true, userColumnsWithDefault...); err != nil { 680 | t.Errorf("Unable to randomize User struct: %s", err) 681 | } 682 | 683 | ctx := context.Background() 684 | tx := MustTx(boil.BeginTx(ctx, nil)) 685 | defer func() { _ = tx.Rollback() }() 686 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 687 | t.Error(err) 688 | } 689 | 690 | slice := UserSlice{o} 691 | 692 | if err = slice.ReloadAll(ctx, tx); err != nil { 693 | t.Error(err) 694 | } 695 | } 696 | 697 | func testUsersSelect(t *testing.T) { 698 | t.Parallel() 699 | 700 | seed := randomize.NewSeed() 701 | var err error 702 | o := &User{} 703 | if err = randomize.Struct(seed, o, userDBTypes, true, userColumnsWithDefault...); err != nil { 704 | t.Errorf("Unable to randomize User struct: %s", err) 705 | } 706 | 707 | ctx := context.Background() 708 | tx := MustTx(boil.BeginTx(ctx, nil)) 709 | defer func() { _ = tx.Rollback() }() 710 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 711 | t.Error(err) 712 | } 713 | 714 | slice, err := Users().All(ctx, tx) 715 | if err != nil { 716 | t.Error(err) 717 | } 718 | 719 | if len(slice) != 1 { 720 | t.Error("want one record, got:", len(slice)) 721 | } 722 | } 723 | 724 | var ( 725 | userDBTypes = map[string]string{`Userid`: `character varying`, `Name`: `character varying`, `Roles`: `character varying`, `Secret`: `character varying`, `Created`: `timestamp with time zone`} 726 | _ = bytes.MinRead 727 | ) 728 | 729 | func testUsersUpdate(t *testing.T) { 730 | t.Parallel() 731 | 732 | if 0 == len(userPrimaryKeyColumns) { 733 | t.Skip("Skipping table with no primary key columns") 734 | } 735 | if len(userAllColumns) == len(userPrimaryKeyColumns) { 736 | t.Skip("Skipping table with only primary key columns") 737 | } 738 | 739 | seed := randomize.NewSeed() 740 | var err error 741 | o := &User{} 742 | if err = randomize.Struct(seed, o, userDBTypes, true, userColumnsWithDefault...); err != nil { 743 | t.Errorf("Unable to randomize User struct: %s", err) 744 | } 745 | 746 | ctx := context.Background() 747 | tx := MustTx(boil.BeginTx(ctx, nil)) 748 | defer func() { _ = tx.Rollback() }() 749 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 750 | t.Error(err) 751 | } 752 | 753 | count, err := Users().Count(ctx, tx) 754 | if err != nil { 755 | t.Error(err) 756 | } 757 | 758 | if count != 1 { 759 | t.Error("want one record, got:", count) 760 | } 761 | 762 | if err = randomize.Struct(seed, o, userDBTypes, true, userPrimaryKeyColumns...); err != nil { 763 | t.Errorf("Unable to randomize User struct: %s", err) 764 | } 765 | 766 | if rowsAff, err := o.Update(ctx, tx, boil.Infer()); err != nil { 767 | t.Error(err) 768 | } else if rowsAff != 1 { 769 | t.Error("should only affect one row but affected", rowsAff) 770 | } 771 | } 772 | 773 | func testUsersSliceUpdateAll(t *testing.T) { 774 | t.Parallel() 775 | 776 | if len(userAllColumns) == len(userPrimaryKeyColumns) { 777 | t.Skip("Skipping table with only primary key columns") 778 | } 779 | 780 | seed := randomize.NewSeed() 781 | var err error 782 | o := &User{} 783 | if err = randomize.Struct(seed, o, userDBTypes, true, userColumnsWithDefault...); err != nil { 784 | t.Errorf("Unable to randomize User struct: %s", err) 785 | } 786 | 787 | ctx := context.Background() 788 | tx := MustTx(boil.BeginTx(ctx, nil)) 789 | defer func() { _ = tx.Rollback() }() 790 | if err = o.Insert(ctx, tx, boil.Infer()); err != nil { 791 | t.Error(err) 792 | } 793 | 794 | count, err := Users().Count(ctx, tx) 795 | if err != nil { 796 | t.Error(err) 797 | } 798 | 799 | if count != 1 { 800 | t.Error("want one record, got:", count) 801 | } 802 | 803 | if err = randomize.Struct(seed, o, userDBTypes, true, userPrimaryKeyColumns...); err != nil { 804 | t.Errorf("Unable to randomize User struct: %s", err) 805 | } 806 | 807 | // Remove Primary keys and unique columns from what we plan to update 808 | var fields []string 809 | if strmangle.StringSliceMatch(userAllColumns, userPrimaryKeyColumns) { 810 | fields = userAllColumns 811 | } else { 812 | fields = strmangle.SetComplement( 813 | userAllColumns, 814 | userPrimaryKeyColumns, 815 | ) 816 | } 817 | 818 | value := reflect.Indirect(reflect.ValueOf(o)) 819 | typ := reflect.TypeOf(o).Elem() 820 | n := typ.NumField() 821 | 822 | updateMap := M{} 823 | for _, col := range fields { 824 | for i := 0; i < n; i++ { 825 | f := typ.Field(i) 826 | if f.Tag.Get("boil") == col { 827 | updateMap[col] = value.Field(i).Interface() 828 | } 829 | } 830 | } 831 | 832 | slice := UserSlice{o} 833 | if rowsAff, err := slice.UpdateAll(ctx, tx, updateMap); err != nil { 834 | t.Error(err) 835 | } else if rowsAff != 1 { 836 | t.Error("wanted one record updated but got", rowsAff) 837 | } 838 | } 839 | 840 | func testUsersUpsert(t *testing.T) { 841 | t.Parallel() 842 | 843 | if len(userAllColumns) == len(userPrimaryKeyColumns) { 844 | t.Skip("Skipping table with only primary key columns") 845 | } 846 | 847 | seed := randomize.NewSeed() 848 | var err error 849 | // Attempt the INSERT side of an UPSERT 850 | o := User{} 851 | if err = randomize.Struct(seed, &o, userDBTypes, true); err != nil { 852 | t.Errorf("Unable to randomize User struct: %s", err) 853 | } 854 | 855 | ctx := context.Background() 856 | tx := MustTx(boil.BeginTx(ctx, nil)) 857 | defer func() { _ = tx.Rollback() }() 858 | if err = o.Upsert(ctx, tx, false, nil, boil.Infer(), boil.Infer()); err != nil { 859 | t.Errorf("Unable to upsert User: %s", err) 860 | } 861 | 862 | count, err := Users().Count(ctx, tx) 863 | if err != nil { 864 | t.Error(err) 865 | } 866 | if count != 1 { 867 | t.Error("want one record, got:", count) 868 | } 869 | 870 | // Attempt the UPDATE side of an UPSERT 871 | if err = randomize.Struct(seed, &o, userDBTypes, false, userPrimaryKeyColumns...); err != nil { 872 | t.Errorf("Unable to randomize User struct: %s", err) 873 | } 874 | 875 | if err = o.Upsert(ctx, tx, true, nil, boil.Infer(), boil.Infer()); err != nil { 876 | t.Errorf("Unable to upsert User: %s", err) 877 | } 878 | 879 | count, err = Users().Count(ctx, tx) 880 | if err != nil { 881 | t.Error(err) 882 | } 883 | if count != 1 { 884 | t.Error("want one record, got:", count) 885 | } 886 | } 887 | -------------------------------------------------------------------------------- /backend/patientdb/sqlboiler.toml: -------------------------------------------------------------------------------- 1 | pkgname="models" 2 | output="models" 3 | schema="public" 4 | [psql] 5 | dbname = "patientdb" 6 | user = "docker" 7 | pass = "docker" 8 | host = "localhost" 9 | sslmode = "disable" 10 | 11 | [aliases.tables.patient_note.columns] 12 | patientid = "Patient_Id" 13 | userid = "User_Id" 14 | -------------------------------------------------------------------------------- /backend/session/handler.go: -------------------------------------------------------------------------------- 1 | package session 2 | 3 | // handles http auth requests for path /session 4 | // jwt token generation based on https://github.com/sohamkamani/jwt-go-example 5 | 6 | import ( 7 | "encoding/json" 8 | "errors" 9 | "fmt" 10 | "time" 11 | 12 | "github.com/davidk81/svelte-golang-demo/backend/user" 13 | "github.com/dgrijalva/jwt-go" 14 | "github.com/valyala/fasthttp" 15 | ) 16 | 17 | // cookie key 18 | const sessionToken = "session-token" 19 | 20 | // TODO: use a secure key mounted during deployment 21 | var jwtKey = []byte("ja93jalkdf092jlkadfh02h3lkdfiu0293lakndf0923haf93ja1h") 22 | 23 | // HandleSession entrypoint http request handler for /session 24 | func HandleSession(ctx *fasthttp.RequestCtx) error { 25 | switch string(ctx.Request.Header.Method()) { 26 | case "POST": 27 | return handleMethodPost(ctx) 28 | case "GET": 29 | return handleMethodGet(ctx) 30 | case "DELETE": 31 | return handleMethodDelete(ctx) 32 | default: 33 | ctx.NotFound() 34 | return nil 35 | } 36 | } 37 | 38 | // HandleRegister entrypoint http request handler for /register 39 | func HandleRegister(ctx *fasthttp.RequestCtx) error { 40 | switch string(ctx.Request.Header.Method()) { 41 | case "POST": 42 | return handleRegisterUser(ctx) 43 | default: 44 | ctx.NotFound() 45 | return nil 46 | } 47 | } 48 | 49 | // logs out user by invalidating session token 50 | func handleMethodDelete(ctx *fasthttp.RequestCtx) error { 51 | var c fasthttp.Cookie 52 | c.SetKey(sessionToken) 53 | c.SetValue("") 54 | c.SetExpire(time.Now()) 55 | ctx.Response.Header.SetCookie(&c) 56 | ctx.SetStatusCode(fasthttp.StatusOK) 57 | return nil 58 | } 59 | 60 | // authenticates user by checking credentials, and sets session token 61 | // if success, responds with user details in post body 62 | func handleMethodPost(ctx *fasthttp.RequestCtx) error { 63 | // decode login credentials from body 64 | var creds Credentials 65 | err := json.Unmarshal(ctx.Request.Body(), &creds) 66 | if err != nil { 67 | ctx.SetStatusCode(fasthttp.StatusBadRequest) 68 | return nil 69 | } 70 | 71 | // check password 72 | user, err := user.Login(creds.Username, creds.Password, ctx) 73 | if err != nil { 74 | return err 75 | } 76 | if user == nil { 77 | ctx.SetStatusCode(fasthttp.StatusUnauthorized) 78 | return nil 79 | } 80 | 81 | // create jwt token 82 | expirationTime := time.Now().Add(60 * time.Minute) 83 | claims := &Claims{ 84 | Username: creds.Username, 85 | StandardClaims: jwt.StandardClaims{ 86 | ExpiresAt: expirationTime.Unix(), 87 | }, 88 | } 89 | 90 | // sign token 91 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 92 | tokenString, err := token.SignedString(jwtKey) 93 | if err != nil { 94 | return err 95 | } 96 | 97 | // update cookie 98 | var c fasthttp.Cookie 99 | c.SetKey(sessionToken) 100 | c.SetValue(tokenString) 101 | c.SetExpire(expirationTime) 102 | ctx.Response.Header.SetCookie(&c) 103 | 104 | // return user info in response, such as roles 105 | b, err := json.Marshal(user) 106 | if err != nil { 107 | return err 108 | } 109 | ctx.SetBody([]byte(b)) 110 | ctx.SetStatusCode(fasthttp.StatusCreated) 111 | return nil 112 | } 113 | 114 | // validates session and returns user info in response body 115 | func handleMethodGet(ctx *fasthttp.RequestCtx) error { 116 | user, err := ValidateSession(ctx) 117 | if err != nil { 118 | ctx.SetStatusCode(fasthttp.StatusUnauthorized) 119 | return nil 120 | } 121 | 122 | // return user info in response, such as roles 123 | b, err := json.Marshal(user) 124 | if err != nil { 125 | return err 126 | } 127 | ctx.SetBody([]byte(b)) 128 | ctx.SetStatusCode(fasthttp.StatusOK) 129 | return nil 130 | } 131 | 132 | // ValidateSession and check user has atleast one of the roles. returns WebUserObject object iff session is valid 133 | func ValidateSession(ctx *fasthttp.RequestCtx, roles ...string) (*user.WebUserObject, error) { 134 | token, err := verifyToken(ctx) 135 | if err != nil { 136 | return nil, err 137 | } 138 | b, err := json.Marshal(token.Claims) 139 | if err != nil { 140 | return nil, err 141 | } 142 | var claims Claims 143 | err = json.Unmarshal(b, &claims) 144 | if err != nil { 145 | return nil, err 146 | } 147 | if claims.ExpiresAt < time.Now().Unix() { 148 | return nil, errors.New("session expired") 149 | } 150 | user, err := user.GetWebUserObject(claims.Username, ctx) 151 | if err != nil { 152 | return nil, err 153 | } 154 | if len(roles) == 0 { 155 | return user, nil 156 | } 157 | for _, requiredRole := range roles { 158 | for _, myRole := range user.Roles { 159 | if requiredRole == myRole { 160 | return user, nil 161 | } 162 | } 163 | } 164 | return nil, errors.New("user doesn't have role") 165 | } 166 | 167 | func verifyToken(ctx *fasthttp.RequestCtx) (*jwt.Token, error) { 168 | tokenString := string(ctx.Request.Header.Cookie(sessionToken)) 169 | token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { 170 | //Make sure that the token method conform to "SigningMethodHMAC" 171 | if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { 172 | return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) 173 | } 174 | return jwtKey, nil 175 | }) 176 | if err != nil { 177 | return nil, err 178 | } 179 | return token, nil 180 | } 181 | 182 | // authenticates user by checking credentials, and sets session token 183 | // if success, responds with user details in post body 184 | func handleRegisterUser(ctx *fasthttp.RequestCtx) error { 185 | // decode login credentials from body 186 | var newuser user.WebUserObject 187 | err := json.Unmarshal(ctx.Request.Body(), &newuser) 188 | if err != nil { 189 | ctx.SetStatusCode(fasthttp.StatusBadRequest) 190 | return nil 191 | } 192 | 193 | // check password 194 | err = user.Register(&newuser, ctx) 195 | if err != nil { 196 | return err 197 | } 198 | 199 | // create jwt token 200 | expirationTime := time.Now().Add(60 * time.Minute) 201 | claims := &Claims{ 202 | Username: newuser.Username, 203 | StandardClaims: jwt.StandardClaims{ 204 | ExpiresAt: expirationTime.Unix(), 205 | }, 206 | } 207 | 208 | // sign token 209 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 210 | tokenString, err := token.SignedString(jwtKey) 211 | if err != nil { 212 | return err 213 | } 214 | 215 | // update cookie 216 | var c fasthttp.Cookie 217 | c.SetKey(sessionToken) 218 | c.SetValue(tokenString) 219 | c.SetExpire(expirationTime) 220 | ctx.Response.Header.SetCookie(&c) 221 | 222 | // return user info in response, such as roles 223 | newuser.Password = "" // sanitize 224 | b, err := json.Marshal(newuser) 225 | if err != nil { 226 | return err 227 | } 228 | ctx.SetBody([]byte(b)) 229 | ctx.SetStatusCode(fasthttp.StatusCreated) 230 | return nil 231 | } 232 | -------------------------------------------------------------------------------- /backend/session/models.go: -------------------------------------------------------------------------------- 1 | package session 2 | 3 | import "github.com/dgrijalva/jwt-go" 4 | 5 | // Credentials struct for demarshalling session post body 6 | type Credentials struct { 7 | Password string `json:"password"` 8 | Username string `json:"username"` 9 | } 10 | 11 | // Claims struct for jwt token contents 12 | type Claims struct { 13 | Username string `json:"username"` 14 | jwt.StandardClaims 15 | } 16 | -------------------------------------------------------------------------------- /backend/user/dao.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/davidk81/svelte-golang-demo/backend/patientdb" 7 | "github.com/davidk81/svelte-golang-demo/backend/patientdb/models" 8 | "github.com/volatiletech/sqlboiler/boil" 9 | ) 10 | 11 | // handles database operations for user table 12 | 13 | // getUser fetches user by userid 14 | func getUser(ctx context.Context, userid string) (*models.User, error) { 15 | return models.Users(models.UserWhere.Userid.EQ(userid)).One(ctx, patientdb.DB()) 16 | } 17 | 18 | // createUser create new user 19 | func createUser(ctx context.Context, user *models.User) error { 20 | return (*user).Insert(ctx, patientdb.DB(), boil.Infer()) 21 | } 22 | -------------------------------------------------------------------------------- /backend/user/handler.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | // handles http requests for /patient and /patients 4 | 5 | import ( 6 | "encoding/json" 7 | "strings" 8 | 9 | "github.com/davidk81/svelte-golang-demo/backend/patientdb/models" 10 | "github.com/valyala/fasthttp" 11 | "golang.org/x/crypto/bcrypt" 12 | ) 13 | 14 | // HandleUser entrypoint http request handler 15 | func HandleUser(ctx *fasthttp.RequestCtx) error { 16 | switch string(ctx.Request.Header.Method()) { 17 | case "POST": 18 | return handleMethodPost(ctx) // not implemented 19 | case "DELETE": 20 | return handleMethodDelete(ctx) // not implemented 21 | default: 22 | ctx.Error("Unsupported path", fasthttp.StatusNotFound) 23 | return nil 24 | } 25 | } 26 | 27 | // Login checks username & password, and returns User data if successful 28 | func Login(username, password string, ctx *fasthttp.RequestCtx) (*WebUserObject, error) { 29 | user, err := getUser(ctx, username) 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | err = verifyPassword(user.Secret, []byte(password)) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | return &WebUserObject{ 40 | Name: user.Name, 41 | Username: user.Userid, 42 | Roles: strings.Split(user.Roles, ","), 43 | }, nil 44 | } 45 | 46 | // GetWebUserObject for retrieving an already validated session 47 | func GetWebUserObject(username string, ctx *fasthttp.RequestCtx) (*WebUserObject, error) { 48 | user, err := getUser(ctx, username) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | return &WebUserObject{ 54 | Name: user.Name, 55 | Username: user.Userid, 56 | Roles: strings.Split(user.Roles, ","), 57 | }, nil 58 | } 59 | 60 | func handleMethodDelete(ctx *fasthttp.RequestCtx) error { 61 | // TODO: 62 | ctx.SetStatusCode(fasthttp.StatusNotImplemented) 63 | return nil 64 | } 65 | 66 | func handleMethodPost(ctx *fasthttp.RequestCtx) error { 67 | // decode post body 68 | var user WebUserObject 69 | err := json.Unmarshal(ctx.Request.Body(), &user) 70 | if err != nil { 71 | ctx.SetStatusCode(fasthttp.StatusBadRequest) 72 | return nil 73 | } 74 | 75 | // return user info in response 76 | b, err := json.Marshal(user) 77 | if err != nil { 78 | return err 79 | } 80 | ctx.SetBody([]byte(b)) 81 | // ctx.SetStatusCode(fasthttp.StatusCreated) 82 | ctx.SetStatusCode(fasthttp.StatusNotImplemented) 83 | return nil 84 | } 85 | 86 | // Register new user 87 | func Register(user *WebUserObject, ctx *fasthttp.RequestCtx) error { 88 | hashedPassword, err := hashAndSaltPassword([]byte(user.Password)) 89 | if err != nil { 90 | return err 91 | } 92 | 93 | userdao := &models.User{ 94 | Userid: user.Username, 95 | Name: user.Name, 96 | Roles: strings.Join(user.Roles, ","), 97 | Secret: hashedPassword, 98 | } 99 | err = createUser(ctx, userdao) 100 | if err != nil { 101 | return err 102 | } 103 | 104 | return nil 105 | } 106 | 107 | // generate a hashed-and-salted password from plain-text password. return value can be stored in db 108 | // https://medium.com/@jcox250/password-hash-salt-using-golang-b041dc94cb72 109 | func hashAndSaltPassword(pwd []byte) (string, error) { 110 | hash, err := bcrypt.GenerateFromPassword(pwd, bcrypt.MinCost) 111 | if err != nil { 112 | return "", err 113 | } 114 | return string(hash), nil 115 | } 116 | 117 | // compaire plain-text password against a hashed-and-salted password 118 | // https://medium.com/@jcox250/password-hash-salt-using-golang-b041dc94cb72 119 | func verifyPassword(hashedPwd string, plainPwd []byte) error { 120 | byteHash := []byte(hashedPwd) 121 | err := bcrypt.CompareHashAndPassword(byteHash, plainPwd) 122 | if err != nil { 123 | return err 124 | } 125 | return nil 126 | } 127 | -------------------------------------------------------------------------------- /backend/user/models.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | ) 7 | 8 | // WebUserObject for responding to api request 9 | type WebUserObject struct { 10 | Name string `json:"name"` 11 | Username string `json:"username"` 12 | Password string `json:"password"` 13 | Roles []string `json:"roles"` 14 | } 15 | 16 | // Validate client supplied data 17 | func (user WebUserObject) Validate(ctx context.Context) error { 18 | if len(user.Name) == 0 { 19 | return errors.New("Name must not be empty") 20 | } 21 | if len(user.Username) < 4 { 22 | return errors.New("Username must be at least 4 characters") 23 | } 24 | if len(user.Password) < 8 { 25 | return errors.New("Password must be at least 8 characters") 26 | } 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /backend/user/user_test.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import "testing" 4 | 5 | func TestPasswordSalt(t *testing.T) { 6 | saltedpw, err := hashAndSaltPassword([]byte("test password")) 7 | if err != nil { 8 | t.Error(err) 9 | } 10 | err = verifyPassword(saltedpw, []byte("test password")) 11 | if err != nil { 12 | t.Errorf("same password failed compare test") 13 | } 14 | err = verifyPassword(saltedpw, []byte("wrong password")) 15 | if err == nil { 16 | t.Errorf("different password should fail compare test but did not") 17 | } 18 | } 19 | 20 | func TestWebUser(t *testing.T) { 21 | user := WebUserObject{ 22 | Name: "", 23 | Username: "abcd", 24 | Password: "12345678", 25 | Roles: []string{"nurse", "admin"}, 26 | } 27 | err := user.Validate(nil) 28 | if err == nil { 29 | t.Errorf("Expected validation error but did not happen") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /db-schema/migrate.sh: -------------------------------------------------------------------------------- 1 | # 2 | # docker-compose up -d 3 | 4 | # 5 | docker run -v `pwd`/migrations:/migrations --network host migrate/migrate:v4.12.1 \ 6 | -path=/migrations/ -database "postgres://docker:docker@localhost:5432/patientdb?sslmode=disable" $* 7 | -------------------------------------------------------------------------------- /db-schema/migrations/1_create_schema.down.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS "patient_note"; 2 | DROP TABLE IF EXISTS "patient"; 3 | DROP TABLE IF EXISTS "user"; 4 | -------------------------------------------------------------------------------- /db-schema/migrations/1_create_schema.up.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE "patient" ( 2 | patientid varchar(36) not null primary key, 3 | name varchar(255) not null, 4 | location varchar(255) not null, 5 | created timestamp with time zone default now() 6 | ); 7 | 8 | CREATE TABLE "user" ( 9 | userid varchar(36) not null primary key, 10 | name varchar(255) not null, 11 | roles varchar(255) not null, 12 | secret varchar(1024) not null, 13 | created timestamp with time zone default now() 14 | ); 15 | 16 | CREATE TABLE "patient_note" ( 17 | noteid char(36) not null primary key, 18 | patientid varchar(100) not null references "patient"(patientid), 19 | userid varchar(100) not null references "user"(userid), 20 | note varchar(4096) not null, 21 | created timestamp with time zone default now() 22 | ); 23 | -------------------------------------------------------------------------------- /db-schema/migrations/99_seed_data.down.sql: -------------------------------------------------------------------------------- 1 | truncate table "patient"; 2 | -------------------------------------------------------------------------------- /db-schema/migrations/99_seed_data.up.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO "patient" (patientid, name, location) 2 | VALUES ('patient1', 'patrick', 'room101'); 3 | 4 | INSERT INTO "patient" (patientid, name, location) 5 | VALUES ('patient2', 'peter', 'room102'); 6 | 7 | INSERT INTO "patient" (patientid, name, location) 8 | VALUES ('patient3', 'paul', 'room103'); 9 | 10 | INSERT INTO "user" (userid, name, roles, secret) 11 | VALUES ('nurse1', 'nancy', 'nurse', '$2a$04$P4ouhozPJZX8NCCm7QyrIe1ZR46HNKL5tZgr0Yn4RCPyY85hnAM0m'); 12 | 13 | INSERT INTO "user" (userid, name, roles, secret) 14 | VALUES ('admin', 'Administrator', 'admin', '$2a$04$P4ouhozPJZX8NCCm7QyrIe1ZR46HNKL5tZgr0Yn4RCPyY85hnAM0m'); 15 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.3" 2 | 3 | services: 4 | db: 5 | image: postgres:11 6 | ports: 7 | - 5432:5432 8 | environment: 9 | - POSTGRES_USER=docker 10 | - POSTGRES_PASSWORD=docker 11 | - PGDATA=/pgdata 12 | - POSTGRES_DB=patientdb 13 | 14 | seed-db: 15 | image: migrate/migrate:v4.12.1 16 | volumes: 17 | - ./db-schema/migrations:/migrations 18 | entrypoint: /bin/sh -c 'sleep 5 && migrate -path=/migrations/ -database "postgres://docker:docker@db:5432/patientdb?sslmode=disable" up' 19 | depends_on: 20 | - "db" 21 | 22 | backend: 23 | build: backend 24 | ports: 25 | - 8000:8000 26 | entrypoint: './main -addr "0.0.0.0:8000" -db "host=db dbname=patientdb user=docker password=docker sslmode=disable"' 27 | depends_on: 28 | - "db" 29 | 30 | frontend: 31 | build: frontend 32 | ports: 33 | - 5000:80 34 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /node_modules/ 3 | /src/node_modules/@sapper/ 4 | yarn-error.log 5 | /cypress/screenshots/ 6 | /__sapper__/ 7 | -------------------------------------------------------------------------------- /frontend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:13.12.0-alpine3.11 as builder 2 | RUN mkdir /build 3 | ADD . /build/ 4 | WORKDIR /build 5 | RUN npm ci 6 | RUN npm run export 7 | 8 | FROM nginx:1.19.1-alpine 9 | COPY --from=builder /build/__sapper__/export/ /usr/share/nginx/html/ 10 | COPY nginx.conf /conf/nginx.conf 11 | CMD nginx -c /conf/nginx.conf 12 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # frontend 2 | 3 | ```sh 4 | # download npm dependencies 5 | npm install 6 | 7 | # run dev mode with hot reload on http://localhost:3000 8 | npm run dev 9 | 10 | # build static pages & serve on http://localhost:5000 11 | npm run export && npx serve __sapper__/export 12 | 13 | # run tests 14 | npm install cypress 15 | npm run test 16 | ``` 17 | -------------------------------------------------------------------------------- /frontend/cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseUrl": "http://localhost:3000", 3 | "video": false 4 | } -------------------------------------------------------------------------------- /frontend/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } -------------------------------------------------------------------------------- /frontend/cypress/integration/spec.js: -------------------------------------------------------------------------------- 1 | describe('Sapper template app', () => { 2 | beforeEach(() => { 3 | cy.visit('/') 4 | }); 5 | 6 | it('has the correct
Health Note: | 24 |{healthNote.note} | 25 |
Nurse ID: | 28 |{healthNote.userid} | 29 |
Date: | 32 |{new Date(healthNote.created)} | 33 |
Name: | 32 |{patient.name} | 33 |
Location: | 36 |{patient.location} | 37 |
{error.message}
37 | 38 | {#if dev && error.stack} 39 |{error.stack}40 | {/if} 41 | -------------------------------------------------------------------------------- /frontend/src/routes/_layout.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 19 | 20 |
This is the 'about' page. There's not much here.
-------------------------------------------------------------------------------- /frontend/src/routes/admin.svelte: -------------------------------------------------------------------------------- 1 |Under construction
-------------------------------------------------------------------------------- /frontend/src/routes/index.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 18 | 19 |Welcome, {$session.profile.name}
27 | {:else} 28 |