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