Commit 593ae11f authored by Brian Conway's avatar Brian Conway

Merge branch 'bconway/refactor_excess_relations' into 'master'

Refactor excess relations in schema

See merge request !15
parents 34dcf8ac f08e4d88
Pipeline #67634153 passed with stage
in 2 minutes and 50 seconds
......@@ -34,7 +34,7 @@ make test
All configuration is available as environmental variables, with defaults defined in `internal/go-resteasy.api/config/config.go`.
```
psql -f config/sql/seed/seed_admin.sql -h localhost re_test postgres
psql -f config/sql/seed/seed_admin.sql -h localhost re_test postgres && \
RE_PGURI=postgres://postgres:postgres@localhost/re_test go-resteasy.api
```
......@@ -46,25 +46,25 @@ API controllers and their usage are outlined in `internal/go-resteasy.api/server
$ curl -s -u admin:hello123 -H "Accept: application/json" \
http://localhost:8080/auth|python -m json.tool
{
"access_keys":[
"accessKeys":[
{
"expires_at":"2019-03-09T04:01:14.641504Z",
"key":"47563a5d-5a83-4225-85b2-022c9cea3522"
"expiresAt":"2019-06-24T22:09:39.112095Z",
"key":"b84547a1-2e7c-48a0-bd7c-fd03805788f9"
}
],
"email":"admin@example.com",
"first_name":"Admin",
"id":"29f3fb31-9f0d-438e-b727-12c85432f603",
"last_name":"User",
"firstName":"Admin",
"id":"17aedc60-ccb4-450b-83cc-ddaad45d1f3e",
"lastName":"User",
"links":[
{
"href":"/users/29f3fb31-9f0d-438e-b727-12c85432f603",
"href":"/users/17aedc60-ccb4-450b-83cc-ddaad45d1f3e",
"rel":"self"
}
],
"login":"admin",
"role":"admin",
"status":"active",
"user_type":"admin",
"zip":"78701"
}
```
......@@ -75,48 +75,48 @@ Need XML? **Not a problem:**
$ curl -s -u admin:hello123 -H "Accept: application/xml" \
http://localhost:8080/auth|xmllint --format -
<?xml version="1.0"?>
<response>
<user>
<id>29f3fb31-9f0d-438e-b727-12c85432f603</id>
<user_type>admin</user_type>
<first_name>Admin</first_name>
<last_name>User</last_name>
<email>admin@example.com</email>
<status>active</status>
<login>admin</login>
<zip>78701</zip>
<access_keys>
<access_key>
<key>47563a5d-5a83-4225-85b2-022c9cea3522</key>
<expires_at>2019-03-09T04:01:14.641504Z</expires_at>
</access_key>
</access_keys>
<links>
<link rel="self" href="/users/29f3fb31-9f0d-438e-b727-12c85432f603"/>
</links>
</user>
</response>
<Response>
<User>
<ID>17aedc60-ccb4-450b-83cc-ddaad45d1f3e</ID>
<Role>admin</Role>
<FirstName>Admin</FirstName>
<LastName>User</LastName>
<Email>admin@example.com</Email>
<Status>active</Status>
<Login>admin</Login>
<Aip>78701</Aip>
<AccessKeys>
<AccessKey>
<Key>b84547a1-2e7c-48a0-bd7c-fd03805788f9</Key>
<ExpiresAt>2019-06-24T22:09:39.112095-05:00</ExpiresAt>
</AccessKey>
</AccessKeys>
<Links>
<Link rel="self" href="/users/17aedc60-ccb4-450b-83cc-ddaad45d1f3e"/>
</Links>
</User>
</Response>
```
To create a new job:
```
$ curl -s -H "Authorization: Bearer 47563a5d-5a83-4225-85b2-022c9cea3522" \
$ curl -s -H "Authorization: Bearer b84547a1-2e7c-48a0-bd7c-fd03805788f9" \
-H "Accept: application/json" -H "Content-Type: application/json" -X POST \
http://localhost:8080/jobs -d \
'{ "started_at": "2015-01-01T19:51:41Z", "completed_at": "2017-01-01T19:51:41Z"}'|\
'{ "startedAt": "2015-01-01T19:51:41Z", "completedAt": "2017-01-01T19:51:41Z"}'|\
python -m json.tool
{
"completed_at":"2017-01-01T19:51:41.696346Z",
"id":"d12a188f-f2c2-4d76-b6fa-b79f79db925f",
"completedAt":"2017-01-01T19:51:41Z",
"id":"46cac275-94dd-4be5-9535-9dad07ba2a8e",
"links":[
{
"href":"/jobs/d12a188f-f2c2-4d76-b6fa-b79f79db925f",
"href":"/jobs/46cac275-94dd-4be5-9535-9dad07ba2a8e",
"rel":"self"
}
],
"started_at":"2015-01-01T19:51:41.696346Z",
"startedAt":"2015-01-01T19:51:41Z",
"status":"ready",
"user_id":"29f3fb31-9f0d-438e-b727-12c85432f603"
"userId":"17aedc60-ccb4-450b-83cc-ddaad45d1f3e"
}
```
DROP TABLE users;
DROP TABLE user_status;
DROP TABLE user_types;
DROP TYPE userstatus;
DROP TYPE userrole;
CREATE TABLE user_types (
id varchar(20) PRIMARY KEY,
description varchar(80) NOT NULL
);
INSERT INTO user_types (id, description)
VALUES ('admin', 'Administrator');
INSERT INTO user_types (id, description)
VALUES ('user', 'User');
CREATE TABLE user_status (
id varchar(20) PRIMARY KEY,
description varchar(80) NOT NULL
);
INSERT INTO user_status (id, description)
VALUES ('active', 'Account enabled');
DROP TYPE IF EXISTS userrole;
CREATE TYPE userrole AS ENUM ('user', 'admin');
INSERT INTO user_status (id, description)
VALUES ('disabled', 'Account disabled');
INSERT INTO user_status (id, description)
VALUES ('deleted', 'Account deleted');
DROP TYPE IF EXISTS userstatus;
CREATE TYPE userstatus AS ENUM ('deleted', 'disabled', 'active');
CREATE TABLE users (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_type_id varchar(20) NOT NULL REFERENCES user_types (id) ON DELETE RESTRICT,
role userrole NOT NULL,
first_name varchar(80) NOT NULL,
last_name varchar(80) NOT NULL,
email varchar(80) NOT NULL UNIQUE,
user_status_id varchar(20) NOT NULL REFERENCES user_status (id) ON DELETE RESTRICT DEFAULT 'active',
status userstatus NOT NULL DEFAULT 'active',
login varchar(40) NOT NULL UNIQUE,
bcrypt_hash varchar(80) NOT NULL,
phone_num varchar(80),
......@@ -39,8 +20,5 @@ CREATE TABLE users (
zip varchar(20)
);
CREATE UNIQUE INDEX users_login_index
ON users(login);
CREATE INDEX users_status_index
ON users(user_status_id);
CREATE UNIQUE INDEX users_login_index ON users(login);
CREATE INDEX users_status_index ON users(status);
CREATE TABLE access_keys (
id SERIAL PRIMARY KEY,
key uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid NOT NULL REFERENCES users (id) ON DELETE CASCADE,
key uuid NOT NULL UNIQUE DEFAULT gen_random_uuid(),
expires_at timestamp without time zone NOT NULL
);
CREATE UNIQUE INDEX access_keys_key_index
ON access_keys(key);
CREATE INDEX access_keys_user_expire_index
ON access_keys(user_id, expires_at);
CREATE INDEX access_keys_user_expires_index ON access_keys(user_id, expires_at);
DROP TABLE jobs;
DROP TABLE job_status;
DROP TYPE jobstatus;
CREATE TABLE job_status (
id varchar(20) PRIMARY KEY,
description varchar(80) NOT NULL
);
INSERT INTO job_status (id, description)
VALUES ('ready', 'Job ready to run');
INSERT INTO job_status (id, description)
VALUES ('running', 'Job currently running');
INSERT INTO job_status (id, description)
VALUES ('complete', 'Job finished');
INSERT INTO job_status (id, description)
VALUES ('failed', 'Job failed to run or while running');
INSERT INTO job_status (id, description)
VALUES ('deleted', 'Job deleted by user');
DROP TYPE IF EXISTS jobstatus;
CREATE TYPE jobstatus AS ENUM ('ready', 'running', 'failed', 'complete', 'deleted');
CREATE TABLE jobs (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid NOT NULL REFERENCES users (id) ON DELETE CASCADE,
job_status_id varchar(20) NOT NULL REFERENCES job_status (id) ON DELETE RESTRICT DEFAULT 'ready',
status jobstatus NOT NULL DEFAULT 'ready',
started_at timestamp without time zone,
completed_at timestamp without time zone
);
CREATE INDEX jobs_user_index
ON jobs(user_id);
CREATE INDEX jobs_status_index
ON jobs(job_status_id);
CREATE INDEX jobs_user_index ON jobs(user_id);
CREATE INDEX jobs_status_index ON jobs(status);
INSERT INTO users (user_type_id, first_name, last_name, email, login, bcrypt_hash, zip) VALUES ('admin', 'Admin', 'User', 'admin@example.com', 'admin', '$2a$10$aawptfp5cj3Te4unv2.tNuVlzfvaEBPP/5cNRx1sgY3DZYGSr9LRq', '78701');
INSERT INTO users (role, first_name, last_name, email, login, bcrypt_hash, zip) VALUES ('admin', 'Admin', 'User', 'admin@example.com', 'admin', '$2a$10$aawptfp5cj3Te4unv2.tNuVlzfvaEBPP/5cNRx1sgY3DZYGSr9LRq', '78701');
......@@ -65,7 +65,7 @@ func (u *User) listUsers(fw response.FormatWriter, client *model.User) {
func (u *User) createUser(fw response.FormatWriter, r *http.Request,
client *model.User) {
// Only admins are allowed to create users.
if client.UserType != model.AdminType {
if client.Role != model.AdminRole {
response.Forbidden(fw, r)
return
}
......@@ -163,7 +163,7 @@ func (u *User) patchUser(fw response.FormatWriter, r *http.Request,
func (u *User) deleteUser(fw response.FormatWriter, r *http.Request,
client *model.User, id string) {
// Only admins are allowed to delete users.
if client.UserType != model.AdminType {
if client.Role != model.AdminRole {
response.Forbidden(fw, r)
return
}
......
package controller
import (
"database/sql"
"net/http"
"gitlab.com/bconway/go-resteasy/internal/go-resteasy.api/middleware"
"gitlab.com/bconway/go-resteasy/internal/go-resteasy.api/model"
"gitlab.com/bconway/go-resteasy/internal/go-resteasy.api/response"
)
// userTypeStore defines the methods used by a UserType controller.
type userTypeStore interface {
ListUserTypes() ([]*model.UserType, error)
ReadUserType(userTypeID string) (*model.UserType, error)
// Satisfy loginStore.
ReadUserByLogin(login string) (*model.User, error)
ReadUserByKey(key string) (*model.User, error)
}
// UserType controller provides methods for UserTypes.
type UserType struct {
Store userTypeStore
}
// UserTypeHandler returns a Handler that processes UserType requests.
func (ut *UserType) UserTypeHandler() *middleware.KeyAuth {
return &middleware.KeyAuth{
Store: ut.Store,
RunFunc: func(fw response.FormatWriter, r *http.Request,
client *model.User) {
switch {
case r.Method == http.MethodGet && r.URL.Path == "":
ut.listUserTypes(fw)
case r.Method == http.MethodGet:
ut.readUserType(fw, r, r.URL.Path)
default:
response.MethodNotAllowed(fw, r)
}
},
}
}
// listUserTypes passes through the request to the store's ListUserTypes.
func (ut *UserType) listUserTypes(fw response.FormatWriter) {
if userTypes, err := ut.Store.ListUserTypes(); err != nil {
fw.WriteError(http.StatusBadRequest, err)
} else {
fw.WriteFormat(http.StatusOK, userTypes)
}
}
// readUserType passes through the request to the store's ReadUserType.
func (ut *UserType) readUserType(fw response.FormatWriter, r *http.Request,
id string) {
if userType, err := ut.Store.ReadUserType(id); err != nil {
if err == sql.ErrNoRows {
response.NotFound(fw, r)
} else {
fw.WriteError(http.StatusBadRequest, err)
}
} else {
fw.WriteFormat(http.StatusOK, userType)
}
}
......@@ -58,7 +58,7 @@ func (b *BasicAuth) AuthRun(fw response.FormatWriter, r *http.Request) {
response.Unauthorized(fw, r)
} else {
fw.SetUser("basic:" + client.Login)
fw.SetUserType(client.UserType)
fw.SetUserType(client.Role)
b.RunFunc(fw, r, client)
}
}
......@@ -57,7 +57,7 @@ func (k *KeyAuth) AuthRun(fw response.FormatWriter, r *http.Request) {
response.Unauthorized(fw, r)
} else {
fw.SetUser("key:" + client.Login)
fw.SetUserType(client.UserType)
fw.SetUserType(client.Role)
k.RunFunc(fw, r, client)
}
}
......@@ -12,5 +12,5 @@ import (
func logHandlerFunc(fw response.FormatWriter, r *http.Request) {
log.Printf("%s %s %s %s %s %d %dms", r.RemoteAddr, fw.User(), fw.UserType(),
r.Method, r.RequestURI, fw.StatusCode(),
time.Since(fw.StartTime())/1000000)
time.Since(fw.StartTime())/time.Millisecond)
}
......@@ -7,6 +7,6 @@ import (
// AccessKey models API keys as defined in table access_keys.
// Key defaults to gen_random_uuid().
type AccessKey struct {
Key string `json:"key" xml:"key"`
ExpiresAt time.Time `json:"expires_at" xml:"expires_at"`
Key string `json:"key" xml:"Key"`
ExpiresAt time.Time `json:"expiresAt" xml:"ExpiresAt"`
}
......@@ -10,21 +10,21 @@ import (
// Error strings used for validation.
const (
jobReqUserID = "required field missing or invalid: user_id"
jobReqUserID = "required field missing or invalid: userId"
)
// Job models jobs as defined in table jobs.
// Status defaults to "ready".
type Job struct {
ID string `json:"id" xml:"id"`
UserID string `json:"user_id" xml:"user_id"`
Status string `json:"status" xml:"status"`
StartedAt *time.Time `json:"started_at,omitempty" xml:"started_at,omitempty"`
CompletedAt *time.Time `json:"completed_at,omitempty" xml:"completed_at,omitempty"`
Links []Link `json:"links" xml:"links>link"`
ID string `json:"id" xml:"ID"`
UserID string `json:"userId" xml:"UserID"`
Status string `json:"status" xml:"Status"`
StartedAt *time.Time `json:"startedAt,omitempty" xml:"StartedAt,omitempty"`
CompletedAt *time.Time `json:"completedAt,omitempty" xml:"CompletedAt,omitempty"`
Links []Link `json:"links" xml:"Links>Link"`
}
// DecodeJob decodes and replaces a passed Job based on payload.
// DecodeJob decodes and replaces/patches a passed Job based on contentType.
func DecodeJob(contentType string, body io.Reader, job *Job) error {
var err error
......
package model
// AdminRole is the administrator user role.
const AdminRole = "admin"
// Link models relations and locations in resources.
type Link struct {
Rel string `json:"rel" xml:"rel,attr"`
......
......@@ -9,9 +9,9 @@ import (
// Error strings used for validation.
const (
userReqUserTypeID = "required field missing or invalid: user_type_id"
userReqFirstName = "required field missing: first_name"
userReqLastName = "required field missing: last_name"
userReqUserTypeID = "required field missing or invalid: role"
userReqFirstName = "required field missing: firstName"
userReqLastName = "required field missing: lastName"
userReqEmail = "required field missing: email"
userReqLogin = "required field missing: login"
userReqPassword = "required field missing: password"
......@@ -21,25 +21,25 @@ const (
// Status defaults to "active". Password is used for client input.
// BcryptHash is never exported.
type User struct {
ID string `json:"id" xml:"id"`
UserType string `json:"user_type" xml:"user_type"`
FirstName string `json:"first_name" xml:"first_name"`
LastName string `json:"last_name" xml:"last_name"`
Email string `json:"email" xml:"email"`
Status string `json:"status" xml:"status"`
Login string `json:"login" xml:"login"`
Password *string `json:"password,omitempty" xml:"password,omitempty"`
ID string `json:"id" xml:"ID"`
Role string `json:"role" xml:"Role"`
FirstName string `json:"firstName" xml:"FirstName"`
LastName string `json:"lastName" xml:"LastName"`
Email string `json:"email" xml:"Email"`
Status string `json:"status" xml:"Status"`
Login string `json:"login" xml:"Login"`
Password *string `json:"password,omitempty" xml:"Password,omitempty"`
BcryptHash string `json:"-" xml:"-"`
PhoneNum *string `json:"phone_num,omitempty" xml:"phone_num,omitempty"`
Address *string `json:"address,omitempty" xml:"address,omitempty"`
City *string `json:"city,omitempty" xml:"city,omitempty"`
State *string `json:"state,omitempty" xml:"state,omitempty"`
Zip *string `json:"zip,omitempty" xml:"zip,omitempty"`
AccessKeys []*AccessKey `json:"access_keys,omitempty" xml:"access_keys>access_key,omitempty"`
Links []Link `json:"links" xml:"links>link"`
PhoneNum *string `json:"phoneNum,omitempty" xml:"PhoneNum,omitempty"`
Address *string `json:"address,omitempty" xml:"Address,omitempty"`
City *string `json:"city,omitempty" xml:"City,omitempty"`
State *string `json:"state,omitempty" xml:"State,omitempty"`
Zip *string `json:"zip,omitempty" xml:"Aip,omitempty"`
AccessKeys []*AccessKey `json:"accessKeys,omitempty" xml:"AccessKeys>AccessKey,omitempty"`
Links []Link `json:"links" xml:"Links>Link"`
}
// DecodeUser decodes and replaces a passed User based on payload.
// DecodeUser decodes and replaces/patches a passed User based on contentType.
func DecodeUser(contentType string, body io.Reader, user *User) error {
var err error
......@@ -58,7 +58,7 @@ func DecodeUser(contentType string, body io.Reader, user *User) error {
// Validate sets an error in the event that a User is not valid.
func (u *User) Validate() error {
switch {
case u.UserType == "":
case u.Role == "":
return errors.New(userReqUserTypeID)
case u.FirstName == "":
return errors.New(userReqFirstName)
......
......@@ -20,27 +20,27 @@ func TestValidateUser(t *testing.T) {
inp User
res error
}{
{User{UserType: "user", FirstName: "First", LastName: "Last",
{User{Role: "user", FirstName: "First", LastName: "Last",
Email: "email@example.com", Login: "flast",
BcryptHash: random.String(10)}, nil},
{User{UserType: "user", FirstName: "First", LastName: "Last",
{User{Role: "user", FirstName: "First", LastName: "Last",
Email: "email@example.com", Login: "flast", Password: &pass}, nil},
{User{FirstName: "First", LastName: "Last", Email: "email@example.com",
Login: "flast", BcryptHash: random.String(10)},
errors.New(userReqUserTypeID)},
{User{UserType: "user", LastName: "Last", Email: "email@example.com",
{User{Role: "user", LastName: "Last", Email: "email@example.com",
Login: "flast", BcryptHash: random.String(10)},
errors.New(userReqFirstName)},
{User{UserType: "user", FirstName: "First", Email: "email@example.com",
{User{Role: "user", FirstName: "First", Email: "email@example.com",
Login: "flast", BcryptHash: random.String(10)},
errors.New(userReqLastName)},
{User{UserType: "user", FirstName: "First", LastName: "Last",
{User{Role: "user", FirstName: "First", LastName: "Last",
Login: "flast", BcryptHash: random.String(10)},
errors.New(userReqEmail)},
{User{UserType: "user", FirstName: "First", LastName: "Last",
{User{Role: "user", FirstName: "First", LastName: "Last",
Email: "email@example.com", BcryptHash: random.String(10)},
errors.New(userReqLogin)},
{User{UserType: "user", FirstName: "First", LastName: "Last",
{User{Role: "user", FirstName: "First", LastName: "Last",
Email: "email@example.com", Login: "flast"},
errors.New(userReqPassword)},
}
......
package model
// AdminType is the administrator UserType's ID.
const AdminType = "admin"
// UserType models user types as defined in table user_types.
type UserType struct {
ID string `json:"id" xml:"id"`
Description string `json:"description" xml:"description"`
Links []Link `json:"links" xml:"links>link"`
}
// PopulateLinks sets the Links slice with a self rel and href URL.
func (ut *UserType) PopulateLinks() {
ut.Links = []Link{{Rel: "self", Href: "/user_types/" + ut.ID}}
}
......@@ -73,10 +73,6 @@ func (xw *XMLWriter) WriteFormat(status int, i interface{}) {
var m interface{}
switch v := i.(type) {
case *model.UserType:
m = xmlUserType{Ut: v}
case []*model.UserType:
m = xmlUserTypes{Uts: v}
case *model.User:
m = xmlUser{U: v}
case []*model.User:
......@@ -104,44 +100,32 @@ func (xw *XMLWriter) WriteError(status int, err error) {
xw.WriteFormat(status, xmlError{Error: err.Error()})
}
// xmlUserType carries a UserType and the response wrapper.
type xmlUserType struct {
XMLName xml.Name `xml:"response"`
Ut *model.UserType `xml:"user_type"`
}
// xmlUserTypes carries a slice of UserTypes and the response wrapper.
type xmlUserTypes struct {
XMLName xml.Name `xml:"response"`
Uts []*model.UserType `xml:"user_types>user_type"`
}
// xmlUser carries a User and the response wrapper.
type xmlUser struct {
XMLName xml.Name `xml:"response"`
U *model.User `xml:"user"`
XMLName xml.Name `xml:"Response"`
U *model.User `xml:"User"`
}
// xmlUsers carries a slice of Users and the response wrapper.
type xmlUsers struct {
XMLName xml.Name `xml:"response"`
Us []*model.User `xml:"users>user"`
XMLName xml.Name `xml:"Response"`
Us []*model.User `xml:"Users>User"`
}
// xmlJob carries a Job and the response wrapper.
type xmlJob struct {
XMLName xml.Name `xml:"response"`
J *model.Job `xml:"job"`
XMLName xml.Name `xml:"Response"`
J *model.Job `xml:"Job"`
}
// xmlJobs carries a slice of Jobs and the response wrapper.
type xmlJobs struct {
XMLName xml.Name `xml:"response"`
Js []*model.Job `xml:"jobs>job"`
XMLName xml.Name `xml:"Response"`
Js []*model.Job `xml:"Jobs>Job"`
}
// xmlError carries an error and the response wrapper.
type xmlError struct {
XMLName xml.Name `xml:"response"`
Error string `xml:"error"`
XMLName xml.Name `xml:"Response"`
Error string `xml:"Error"`
}
......@@ -21,7 +21,6 @@ func New(cfg *config.Config) *http.Server {
// Instantiate controllers.
auth := &controller.Auth{Store: s}
userType := &controller.UserType{Store: s}
user := &controller.User{Store: s}
job := &controller.Job{Store: s}
......@@ -30,17 +29,9 @@ func New(cfg *config.Config) *http.Server {
smux := http.NewServeMux()
smux.Handle("/", controller.RootHandler())
smux.Handle("/auth", http.StripPrefix("/auth", auth.AuthHandler()))
smux.Handle("/user_types", http.StripPrefix("/user_types",
userType.UserTypeHandler()))
smux.Handle("/user_types/", http.StripPrefix("/user_types/",
userType.UserTypeHandler()))
smux.Handle("/users", http.StripPrefix("/users", user.UserHandler()))
smux.Handle("/users/", http.StripPrefix("/users/", user.UserHandler()))
smux.Handle("/jobs", http.StripPrefix("/jobs", job.JobHandler()))
smux.Handle("/jobs/", http.StripPrefix("/jobs/", job.JobHandler()))
return &http.Server{Addr: ":8080", Handler: smux}
}
......@@ -14,13 +14,12 @@ RETURNING key
// CreateAccessKey creates a new AccessKey for a known user ID.
func (s *Store) CreateAccessKey(userID string) (*model.AccessKey, error) {
ak := &model.AccessKey{ExpiresAt: time.Now().UTC().Add(24 * time.Hour)}
ak := &model.AccessKey{ExpiresAt: time.Now().Add(24 * time.Hour)}
if err := s.Db.QueryRow(createAccessKey, userID, ak.ExpiresAt).Scan(
&ak.Key); err != nil {
return nil, err
}
return ak, nil
}
......@@ -52,7 +51,6 @@ func (s *Store) ListAccessKeys(userID string) ([]*model.AccessKey, error) {
if err = rows.Err(); err != nil {
return nil, err
}
return keys, nil
}
......@@ -63,13 +61,12 @@ func (s *Store) RefreshAccessKeys(userID string) ([]*model.AccessKey, error) {
return nil, err
}
if keys == nil || keys[0].ExpiresAt.Before(time.Now().UTC().Add(time.Hour)) {
if _, err = s.CreateAccessKey(userID); err != nil {
return nil, err
}
if keys, err = s.ListAccessKeys(userID); err != nil {
if len(keys) == 0 || keys[0].ExpiresAt.Before(time.Now().Add(time.Hour)) {
key, err := s.CreateAccessKey(userID)
if err != nil {
return nil, err
}
keys = append([]*model.AccessKey{key}, keys...)
}
return keys, nil
}
......@@ -16,7 +16,7 @@ RETURNING id
// CreateJob validates and creates a new Job.
func (s *Store) CreateJob(client *model.User, j *model.Job) error {
// Users are not allowed to set another UserId.
if j.UserID == "" || client.UserType == "user" {
if j.UserID == "" || client.Role == "user" {
j.UserID = client.ID
}
......@@ -35,7 +35,7 @@ func (s *Store) CreateJob(client *model.User, j *model.Job) error {
}
const readJob = `
SELECT id, user_id, job_status_id, started_at, completed_at
SELECT id, user_id, status, started_at, completed_at
FROM jobs
WHERE id = $1
`
......@@ -44,7 +44,7 @@ WHERE id = $1
func (s *Store) ReadJob(client *model.User, jobID string) (*model.Job, error) {
var row *sql.Row
if client.UserType == model.AdminType {
if client.Role == model.AdminRole {
row = s.Db.QueryRow(readJob, jobID)
} else {
row = s.Db.QueryRow(readJob+" AND user_id = $2", jobID, client.ID)
......@@ -70,7 +70,7 @@ WHERE id = $4
// representation.
func (s *Store) UpdateJob(client *model.User, j *model.Job) error {
// Users are not allowed to set another UserId.
if j.UserID == "" || client.UserType == "user" {
if j.UserID == "" || client.Role == "user" {
j.UserID = client.ID
}
......@@ -89,7 +89,7 @@ func (s *Store) UpdateJob(client *model.User, j *model.Job) error {
const deleteJob = `
UPDATE jobs
SET job_status_id = 'deleted'
SET status = 'deleted'
WHERE id = $1
`
......@@ -100,9 +100,9 @@ func (s *Store) DeleteJob(jobID string) error {
}
const listJobs = `
SELECT id, user_id, job_status_id, started_at, completed_at
SELECT id, user_id, status, started_at, completed_at
FROM jobs