scopes
2015-12-13
scopes/scope_postgres.go
Port over Postgres storage and tests. Do the minimum possible port of the code from auth to get Postgres working and make the tests run and pass again. This leaves a bug where the ErrScopeAlreadyExists type, when populatd from Postgres, contains the entire error string returned from the database, instead of parsing the ID itself out. Which is a thing we should do and add a test for.
| paddy@2 | 1 package scopes |
| paddy@2 | 2 |
| paddy@2 | 3 import ( |
| paddy@2 | 4 "code.secondbit.org/scopes.hg/types" |
| paddy@2 | 5 "golang.org/x/net/context" |
| paddy@2 | 6 |
| paddy@2 | 7 "github.com/lib/pq" |
| paddy@2 | 8 "github.com/secondbit/pan" |
| paddy@2 | 9 ) |
| paddy@2 | 10 |
| paddy@2 | 11 func (p *postgres) createScopesSQL(scopes []scopeTypes.Scope) *pan.Query { |
| paddy@2 | 12 fields, _ := pan.GetFields(scopes[0]) |
| paddy@2 | 13 query := pan.New(pan.POSTGRES, "INSERT INTO "+pan.GetTableName(scopes[0])) |
| paddy@2 | 14 query.Include("(" + pan.QueryList(fields) + ")") |
| paddy@2 | 15 query.Include("VALUES") |
| paddy@2 | 16 query.FlushExpressions(" ") |
| paddy@2 | 17 for _, scope := range scopes { |
| paddy@2 | 18 _, values := pan.GetFields(scope) |
| paddy@2 | 19 query.Include("("+pan.VariableList(len(values))+")", values...) |
| paddy@2 | 20 } |
| paddy@2 | 21 return query.FlushExpressions(", ") |
| paddy@2 | 22 } |
| paddy@2 | 23 |
| paddy@2 | 24 func (p *postgres) CreateScopes(scopes []scopeTypes.Scope, ctx context.Context) error { |
| paddy@2 | 25 if len(scopes) < 1 { |
| paddy@2 | 26 return nil |
| paddy@2 | 27 } |
| paddy@2 | 28 query := p.createScopesSQL(scopes) |
| paddy@2 | 29 _, err := p.db.Exec(query.String(), query.Args...) |
| paddy@2 | 30 if e, ok := err.(*pq.Error); ok && e.Constraint == "scopes_pkey" { |
| paddy@2 | 31 // BUG(paddy): we need to parse e.Detail to pull out the duplicate Scope ID |
| paddy@2 | 32 err = ErrScopeAlreadyExists(e.Detail) |
| paddy@2 | 33 } |
| paddy@2 | 34 return err |
| paddy@2 | 35 } |
| paddy@2 | 36 |
| paddy@2 | 37 func (p *postgres) getScopesSQL(ids []string) *pan.Query { |
| paddy@2 | 38 var scope scopeTypes.Scope |
| paddy@2 | 39 intids := make([]interface{}, len(ids)) |
| paddy@2 | 40 for pos, id := range ids { |
| paddy@2 | 41 intids[pos] = id |
| paddy@2 | 42 } |
| paddy@2 | 43 fields, _ := pan.GetFields(scope) |
| paddy@2 | 44 query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(scope)) |
| paddy@2 | 45 query.IncludeWhere() |
| paddy@2 | 46 query.Include(pan.GetUnquotedColumn(scope, "ID") + " IN") |
| paddy@2 | 47 query.Include("("+pan.VariableList(len(ids))+")", intids...) |
| paddy@2 | 48 return query.FlushExpressions(" ") |
| paddy@2 | 49 } |
| paddy@2 | 50 |
| paddy@2 | 51 func (p *postgres) GetScopes(ids []string, ctx context.Context) (map[string]scopeTypes.Scope, error) { |
| paddy@2 | 52 query := p.getScopesSQL(ids) |
| paddy@2 | 53 rows, err := p.db.Query(query.String(), query.Args...) |
| paddy@2 | 54 if err != nil { |
| paddy@2 | 55 return map[string]scopeTypes.Scope{}, err |
| paddy@2 | 56 } |
| paddy@2 | 57 scopes := map[string]scopeTypes.Scope{} |
| paddy@2 | 58 for rows.Next() { |
| paddy@2 | 59 var scope scopeTypes.Scope |
| paddy@2 | 60 err := pan.Unmarshal(rows, &scope) |
| paddy@2 | 61 if err != nil { |
| paddy@2 | 62 return scopes, err |
| paddy@2 | 63 } |
| paddy@2 | 64 scopes[scope.ID] = scope |
| paddy@2 | 65 } |
| paddy@2 | 66 if err = rows.Err(); err != nil { |
| paddy@2 | 67 return scopes, err |
| paddy@2 | 68 } |
| paddy@2 | 69 return scopes, nil |
| paddy@2 | 70 } |
| paddy@2 | 71 |
| paddy@2 | 72 func (p *postgres) updateScopeSQL(change scopeTypes.ScopeChange) *pan.Query { |
| paddy@2 | 73 var scope scopeTypes.Scope |
| paddy@2 | 74 query := pan.New(pan.POSTGRES, "UPDATE "+pan.GetTableName(scope)+" SET ") |
| paddy@2 | 75 query.IncludeIfNotNil(pan.GetUnquotedColumn(scope, "Name")+" = ?", change.Name) |
| paddy@2 | 76 query.IncludeIfNotNil(pan.GetUnquotedColumn(scope, "Description")+" = ?", change.Description) |
| paddy@2 | 77 query.FlushExpressions(", ") |
| paddy@2 | 78 query.IncludeWhere() |
| paddy@2 | 79 query.Include(pan.GetUnquotedColumn(scope, "ID")+" = ?", change.ScopeID) |
| paddy@2 | 80 return query.FlushExpressions(" ") |
| paddy@2 | 81 } |
| paddy@2 | 82 |
| paddy@2 | 83 func (p *postgres) UpdateScope(change scopeTypes.ScopeChange, ctx context.Context) error { |
| paddy@2 | 84 if change.Empty() { |
| paddy@2 | 85 return nil |
| paddy@2 | 86 } |
| paddy@2 | 87 query := p.updateScopeSQL(change) |
| paddy@2 | 88 res, err := p.db.Exec(query.String(), query.Args...) |
| paddy@2 | 89 if err != nil { |
| paddy@2 | 90 return err |
| paddy@2 | 91 } |
| paddy@2 | 92 rows, err := res.RowsAffected() |
| paddy@2 | 93 if err != nil { |
| paddy@2 | 94 return err |
| paddy@2 | 95 } |
| paddy@2 | 96 if rows < 1 { |
| paddy@2 | 97 return ErrScopeNotFound |
| paddy@2 | 98 } |
| paddy@2 | 99 return err |
| paddy@2 | 100 } |
| paddy@2 | 101 |
| paddy@2 | 102 func (p *postgres) removeScopesSQL(ids []string) *pan.Query { |
| paddy@2 | 103 var scope scopeTypes.Scope |
| paddy@2 | 104 intids := make([]interface{}, len(ids)) |
| paddy@2 | 105 for pos, id := range ids { |
| paddy@2 | 106 intids[pos] = id |
| paddy@2 | 107 } |
| paddy@2 | 108 query := pan.New(pan.POSTGRES, "DELETE FROM "+pan.GetTableName(scope)) |
| paddy@2 | 109 query.IncludeWhere() |
| paddy@2 | 110 query.Include(pan.GetUnquotedColumn(scope, "ID") + " IN") |
| paddy@2 | 111 query.Include("("+pan.VariableList(len(ids))+")", intids...) |
| paddy@2 | 112 return query.FlushExpressions(" ") |
| paddy@2 | 113 } |
| paddy@2 | 114 |
| paddy@2 | 115 func (p *postgres) RemoveScopes(ids []string, ctx context.Context) error { |
| paddy@2 | 116 query := p.removeScopesSQL(ids) |
| paddy@2 | 117 res, err := p.db.Exec(query.String(), query.Args...) |
| paddy@2 | 118 if err != nil { |
| paddy@2 | 119 return err |
| paddy@2 | 120 } |
| paddy@2 | 121 rows, err := res.RowsAffected() |
| paddy@2 | 122 if err != nil { |
| paddy@2 | 123 return err |
| paddy@2 | 124 } |
| paddy@2 | 125 if rows < 1 { |
| paddy@2 | 126 return ErrScopeNotFound |
| paddy@2 | 127 } |
| paddy@2 | 128 return nil |
| paddy@2 | 129 } |
| paddy@2 | 130 |
| paddy@2 | 131 func (p *postgres) listScopesSQL() *pan.Query { |
| paddy@2 | 132 var scope scopeTypes.Scope |
| paddy@2 | 133 fields, _ := pan.GetFields(scope) |
| paddy@2 | 134 query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(scope)) |
| paddy@2 | 135 return query.FlushExpressions(" ") |
| paddy@2 | 136 } |
| paddy@2 | 137 |
| paddy@2 | 138 func (p *postgres) ListScopes(ctx context.Context) ([]scopeTypes.Scope, error) { |
| paddy@2 | 139 query := p.listScopesSQL() |
| paddy@2 | 140 rows, err := p.db.Query(query.String(), query.Args...) |
| paddy@2 | 141 if err != nil { |
| paddy@2 | 142 return []scopeTypes.Scope{}, err |
| paddy@2 | 143 } |
| paddy@2 | 144 var scopes []scopeTypes.Scope |
| paddy@2 | 145 for rows.Next() { |
| paddy@2 | 146 var scope scopeTypes.Scope |
| paddy@2 | 147 err = pan.Unmarshal(rows, &scope) |
| paddy@2 | 148 if err != nil { |
| paddy@2 | 149 return scopes, err |
| paddy@2 | 150 } |
| paddy@2 | 151 scopes = append(scopes, scope) |
| paddy@2 | 152 } |
| paddy@2 | 153 if err = rows.Err(); err != nil { |
| paddy@2 | 154 return scopes, err |
| paddy@2 | 155 } |
| paddy@2 | 156 return scopes, nil |
| paddy@2 | 157 } |