scopes

Paddy 2015-12-13

2:a64a25ae2db1 Go to Latest

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.

History
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/scope_postgres.go	Sun Dec 13 20:42:48 2015 -0800
     1.3 @@ -0,0 +1,157 @@
     1.4 +package scopes
     1.5 +
     1.6 +import (
     1.7 +	"code.secondbit.org/scopes.hg/types"
     1.8 +	"golang.org/x/net/context"
     1.9 +
    1.10 +	"github.com/lib/pq"
    1.11 +	"github.com/secondbit/pan"
    1.12 +)
    1.13 +
    1.14 +func (p *postgres) createScopesSQL(scopes []scopeTypes.Scope) *pan.Query {
    1.15 +	fields, _ := pan.GetFields(scopes[0])
    1.16 +	query := pan.New(pan.POSTGRES, "INSERT INTO "+pan.GetTableName(scopes[0]))
    1.17 +	query.Include("(" + pan.QueryList(fields) + ")")
    1.18 +	query.Include("VALUES")
    1.19 +	query.FlushExpressions(" ")
    1.20 +	for _, scope := range scopes {
    1.21 +		_, values := pan.GetFields(scope)
    1.22 +		query.Include("("+pan.VariableList(len(values))+")", values...)
    1.23 +	}
    1.24 +	return query.FlushExpressions(", ")
    1.25 +}
    1.26 +
    1.27 +func (p *postgres) CreateScopes(scopes []scopeTypes.Scope, ctx context.Context) error {
    1.28 +	if len(scopes) < 1 {
    1.29 +		return nil
    1.30 +	}
    1.31 +	query := p.createScopesSQL(scopes)
    1.32 +	_, err := p.db.Exec(query.String(), query.Args...)
    1.33 +	if e, ok := err.(*pq.Error); ok && e.Constraint == "scopes_pkey" {
    1.34 +		// BUG(paddy): we need to parse e.Detail to pull out the duplicate Scope ID
    1.35 +		err = ErrScopeAlreadyExists(e.Detail)
    1.36 +	}
    1.37 +	return err
    1.38 +}
    1.39 +
    1.40 +func (p *postgres) getScopesSQL(ids []string) *pan.Query {
    1.41 +	var scope scopeTypes.Scope
    1.42 +	intids := make([]interface{}, len(ids))
    1.43 +	for pos, id := range ids {
    1.44 +		intids[pos] = id
    1.45 +	}
    1.46 +	fields, _ := pan.GetFields(scope)
    1.47 +	query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(scope))
    1.48 +	query.IncludeWhere()
    1.49 +	query.Include(pan.GetUnquotedColumn(scope, "ID") + " IN")
    1.50 +	query.Include("("+pan.VariableList(len(ids))+")", intids...)
    1.51 +	return query.FlushExpressions(" ")
    1.52 +}
    1.53 +
    1.54 +func (p *postgres) GetScopes(ids []string, ctx context.Context) (map[string]scopeTypes.Scope, error) {
    1.55 +	query := p.getScopesSQL(ids)
    1.56 +	rows, err := p.db.Query(query.String(), query.Args...)
    1.57 +	if err != nil {
    1.58 +		return map[string]scopeTypes.Scope{}, err
    1.59 +	}
    1.60 +	scopes := map[string]scopeTypes.Scope{}
    1.61 +	for rows.Next() {
    1.62 +		var scope scopeTypes.Scope
    1.63 +		err := pan.Unmarshal(rows, &scope)
    1.64 +		if err != nil {
    1.65 +			return scopes, err
    1.66 +		}
    1.67 +		scopes[scope.ID] = scope
    1.68 +	}
    1.69 +	if err = rows.Err(); err != nil {
    1.70 +		return scopes, err
    1.71 +	}
    1.72 +	return scopes, nil
    1.73 +}
    1.74 +
    1.75 +func (p *postgres) updateScopeSQL(change scopeTypes.ScopeChange) *pan.Query {
    1.76 +	var scope scopeTypes.Scope
    1.77 +	query := pan.New(pan.POSTGRES, "UPDATE "+pan.GetTableName(scope)+" SET ")
    1.78 +	query.IncludeIfNotNil(pan.GetUnquotedColumn(scope, "Name")+" = ?", change.Name)
    1.79 +	query.IncludeIfNotNil(pan.GetUnquotedColumn(scope, "Description")+" = ?", change.Description)
    1.80 +	query.FlushExpressions(", ")
    1.81 +	query.IncludeWhere()
    1.82 +	query.Include(pan.GetUnquotedColumn(scope, "ID")+" = ?", change.ScopeID)
    1.83 +	return query.FlushExpressions(" ")
    1.84 +}
    1.85 +
    1.86 +func (p *postgres) UpdateScope(change scopeTypes.ScopeChange, ctx context.Context) error {
    1.87 +	if change.Empty() {
    1.88 +		return nil
    1.89 +	}
    1.90 +	query := p.updateScopeSQL(change)
    1.91 +	res, err := p.db.Exec(query.String(), query.Args...)
    1.92 +	if err != nil {
    1.93 +		return err
    1.94 +	}
    1.95 +	rows, err := res.RowsAffected()
    1.96 +	if err != nil {
    1.97 +		return err
    1.98 +	}
    1.99 +	if rows < 1 {
   1.100 +		return ErrScopeNotFound
   1.101 +	}
   1.102 +	return err
   1.103 +}
   1.104 +
   1.105 +func (p *postgres) removeScopesSQL(ids []string) *pan.Query {
   1.106 +	var scope scopeTypes.Scope
   1.107 +	intids := make([]interface{}, len(ids))
   1.108 +	for pos, id := range ids {
   1.109 +		intids[pos] = id
   1.110 +	}
   1.111 +	query := pan.New(pan.POSTGRES, "DELETE FROM "+pan.GetTableName(scope))
   1.112 +	query.IncludeWhere()
   1.113 +	query.Include(pan.GetUnquotedColumn(scope, "ID") + " IN")
   1.114 +	query.Include("("+pan.VariableList(len(ids))+")", intids...)
   1.115 +	return query.FlushExpressions(" ")
   1.116 +}
   1.117 +
   1.118 +func (p *postgres) RemoveScopes(ids []string, ctx context.Context) error {
   1.119 +	query := p.removeScopesSQL(ids)
   1.120 +	res, err := p.db.Exec(query.String(), query.Args...)
   1.121 +	if err != nil {
   1.122 +		return err
   1.123 +	}
   1.124 +	rows, err := res.RowsAffected()
   1.125 +	if err != nil {
   1.126 +		return err
   1.127 +	}
   1.128 +	if rows < 1 {
   1.129 +		return ErrScopeNotFound
   1.130 +	}
   1.131 +	return nil
   1.132 +}
   1.133 +
   1.134 +func (p *postgres) listScopesSQL() *pan.Query {
   1.135 +	var scope scopeTypes.Scope
   1.136 +	fields, _ := pan.GetFields(scope)
   1.137 +	query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(scope))
   1.138 +	return query.FlushExpressions(" ")
   1.139 +}
   1.140 +
   1.141 +func (p *postgres) ListScopes(ctx context.Context) ([]scopeTypes.Scope, error) {
   1.142 +	query := p.listScopesSQL()
   1.143 +	rows, err := p.db.Query(query.String(), query.Args...)
   1.144 +	if err != nil {
   1.145 +		return []scopeTypes.Scope{}, err
   1.146 +	}
   1.147 +	var scopes []scopeTypes.Scope
   1.148 +	for rows.Next() {
   1.149 +		var scope scopeTypes.Scope
   1.150 +		err = pan.Unmarshal(rows, &scope)
   1.151 +		if err != nil {
   1.152 +			return scopes, err
   1.153 +		}
   1.154 +		scopes = append(scopes, scope)
   1.155 +	}
   1.156 +	if err = rows.Err(); err != nil {
   1.157 +		return scopes, err
   1.158 +	}
   1.159 +	return scopes, nil
   1.160 +}