diff --git a/profile/datastore.go b/profile/datastore.go deleted file mode 100644 index 1d18bf39..00000000 --- a/profile/datastore.go +++ /dev/null @@ -1,135 +0,0 @@ -package profile - -import ( - "database/sql" - "fmt" - "strings" - "time" - - kitlog "github.com/go-kit/kit/log" - "github.com/jmoiron/sqlx" - _ "github.com/lib/pq" // postgres driver - "github.com/pkg/errors" -) - -// ErrExists is returned if a profile already exists -var ErrExists = errors.New("profile already exists. each profile must have a unique identifier") - -// Datastore manages profiles in a database -type Datastore interface { - // Add adds a profile to the datastore, - // If a profile already exists, an error will be returned - Add(pr *Profile) (*Profile, error) - - // Profiles can query the datastore for one or more profiles - // and accepts one or more params as filters - // Example: Profiles(Identifier{"com.example.id")} - Profiles(params ...interface{}) ([]Profile, error) -} - -// whereer is for building args passed into Profiles() -type whereer interface { - where() string -} - -// Identifier is a PayloadIdentifier filter which can be passed as a param to Profiles() -type Identifier struct{ PayloadIdentifier string } - -func (p Identifier) where() string { - return fmt.Sprintf("identifier='%s'", p.PayloadIdentifier) -} - -// UUID is a Profile UUID filter which can be passed as a param to Profiles() -type UUID struct{ UUID string } - -func (p UUID) where() string { - return fmt.Sprintf("profile_uuid='%s'", p.UUID) -} - -type pgStore struct { - *sqlx.DB -} - -func (store pgStore) Add(prf *Profile) (*Profile, error) { - err := store.QueryRow(addProfileStmt, prf.PayloadIdentifier, prf.ProfileData).Scan(&prf.UUID) - if err == sql.ErrNoRows { - return nil, ErrExists - } - if err != nil { - return nil, errors.Wrap(err, "pgStore add profile") - } - return prf, nil -} - -func (store pgStore) Profiles(params ...interface{}) ([]Profile, error) { - stmt := selectProfilesStmt - var where []string - for _, param := range params { - if f, ok := param.(whereer); ok { - where = append(where, f.where()) - } - } - - if len(where) != 0 { - whereFilter := strings.Join(where, ",") - stmt = fmt.Sprintf("%s WHERE %s", selectProfilesStmt, whereFilter) - } - - var profiles []Profile - err := store.Select(&profiles, stmt) - if err != nil { - return nil, errors.Wrap(err, "pgStore Profiles") - } - - return profiles, nil - -} - -//NewDB creates a Datastore -func NewDB(driver, conn string, logger kitlog.Logger) (Datastore, error) { - switch driver { - case "postgres": - db, err := sqlx.Open(driver, conn) - if err != nil { - return nil, errors.Wrap(err, "profile datastore") - } - var dbError error - maxAttempts := 20 - for attempts := 1; attempts <= maxAttempts; attempts++ { - dbError = db.Ping() - if dbError == nil { - break - } - logger.Log("msg", fmt.Sprintf("could not connect to postgres: %v", dbError)) - time.Sleep(time.Duration(attempts) * time.Second) - } - if dbError != nil { - return nil, errors.Wrap(dbError, "profile datastore") - } - migrate(db) - return pgStore{DB: db}, nil - default: - return nil, errors.New("unknown driver") - } -} - -// sql statements -var ( - addProfileStmt = `INSERT INTO profiles (identifier, profile_data) VALUES ($1, $2) - ON CONFLICT ON CONSTRAINT profiles_identifier_key DO NOTHING - RETURNING profile_uuid;` - selectProfilesStmt = `SELECT profile_uuid, identifier FROM profiles` -) - -func migrate(db *sqlx.DB) { - schema := ` - CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; - CREATE TABLE IF NOT EXISTS profiles ( - profile_uuid uuid PRIMARY KEY - DEFAULT uuid_generate_v4(), - identifier text UNIQUE NOT NULL, - profile_data bytea - );` - - db.MustExec(schema) -} diff --git a/profile/datastore_test.go b/profile/datastore_test.go deleted file mode 100644 index c30c8f41..00000000 --- a/profile/datastore_test.go +++ /dev/null @@ -1,225 +0,0 @@ -package profile - -import ( - "fmt" - "math/rand" - "os" - "reflect" - "testing" - "testing/quick" - "time" - - "github.com/go-kit/kit/log" - "github.com/jmoiron/sqlx" -) - -func TestNewDB(t *testing.T) { - defer teardown() - logger := log.NewLogfmtLogger(os.Stderr) - _, err := NewDB("postgres", testConn, logger) - if err != nil { - t.Fatal(err) - } -} - -// TestAdd generates a bunch of new profiles -// and adds them to the database. -// The test checks that the profile stored is the same as the one returned. -func TestDatastoreAdd(t *testing.T) { - ds := datastore(t) - defer teardown() - - assertion := func(pf Profile) bool { - newPrf, err := ds.Add(&pf) - if err != nil { - t.Fatal(err) - return false - } - if newPrf.UUID == "" || newPrf.PayloadIdentifier != pf.PayloadIdentifier { - return false - } - return true - } - - if err := quick.Check(assertion, nil); err != nil { - t.Error(err) - } -} - -func TestDatastoreProfiles(t *testing.T) { - ds := datastore(t) - defer teardown() - - // test with empty table - testReturnNoArgs(t, ds, 0) - - // insert some profiles and retrieve them - var profileCount = 5 - addRandomProfiles(profileCount, ds, t) - profiles, err := ds.Profiles() - if err != nil { - t.Fatal(err) - } - - returnedCount := len(profiles) - if returnedCount != profileCount { - t.Fatal("expected", profileCount, "got", returnedCount) - } - - // build data for remaining tests - var allIdentifiers []string - var allUUIDs []string - for _, p := range profiles { - allIdentifiers = append(allIdentifiers, p.PayloadIdentifier) - allUUIDs = append(allUUIDs, p.UUID) - } - - // return everything - testReturnNoArgs(t, ds, profileCount) - // test nonexisting profile - testReturnNone(t, ds) - // test returning a unique id - testReturnOneByIdentifier(t, ds, allIdentifiers[0]) - // test returning a unique profile by UUID - testReturnOneByUUID(t, ds, allUUIDs[0]) - -} -func testReturnNoArgs(t *testing.T, ds Datastore, expectCount int) { - profiles, err := ds.Profiles() - if err != nil { - t.Fatal(err) - } - - returnedCount := len(profiles) - if returnedCount != expectCount { - t.Fatal("expected", expectCount, "got", returnedCount) - } -} - -func testReturnNone(t *testing.T, ds Datastore) { - identifier := "com.example.does.not.exist" - profiles, err := ds.Profiles(Identifier{identifier}) - if err != nil { - t.Fatal(err) - } - - returnedCount := len(profiles) - if returnedCount != 0 { - t.Fatal("expected", 0, "got", returnedCount) - } -} - -func testReturnOneByIdentifier(t *testing.T, ds Datastore, id string) { - profiles, err := ds.Profiles(Identifier{id}) - if err != nil { - t.Fatal(err) - } - - returnedCount := len(profiles) - if returnedCount != 1 { - t.Fatal("expected", 1, "got", returnedCount) - } - - returnedIdentifier := profiles[0].PayloadIdentifier - if returnedIdentifier != id { - t.Fatal("expected", id, "got", returnedIdentifier) - } -} - -func testReturnOneByUUID(t *testing.T, ds Datastore, uuid string) { - profiles, err := ds.Profiles(UUID{uuid}) - if err != nil { - t.Fatal(err) - } - - returnedCount := len(profiles) - if returnedCount != 1 { - t.Fatal("expected", 1, "got", returnedCount) - } - - returnedUUID := profiles[0].UUID - if returnedUUID != uuid { - t.Fatal("expected", uuid, "got", returnedUUID) - } -} - -func addRandomProfiles(num int, ds Datastore, t *testing.T) { - for i := 1; i <= num; i++ { - pf := randomProfile() - _, err := ds.Add(&pf) - if err != nil { - t.Fatal(err) - } - } -} - -// helpers -func randomProfile() Profile { - vrf, ok := quick.Value(reflect.TypeOf(Profile{}), rand.New(rand.NewSource(1))) - if !ok { - panic("randomProfile: no value") - } - - if f, ok := vrf.Interface().(Profile); ok { - return f - } - return Profile{} -} - -// implement quick.Generator for Profile -func (pf Profile) Generate(rand *rand.Rand, size int) reflect.Value { - a := RandomString(16) - b := RandomString(16) - c := RandomString(16) - randomIdnetifier := fmt.Sprintf("%v.%v.%v", a, b, c) - randomProfile := Profile{ - PayloadIdentifier: randomIdnetifier, - ProfileData: `PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPCFET0NUWVBFIHBsaXN0IFBVQkxJQyAiLS8vQXBwbGUvL0RURCBQTElTVCAxLjAvL0VOIiAiaHR0cDovL3d3dy5hcHBsZS5jb20vRFREcy9Qcm9wZXJ0eUxpc3QtMS4wLmR0ZCI+CjxwbGlzdCB2ZXJzaW9uPSIxLjAiPgo8ZGljdD4KICAgIDxrZXk+UGF5bG9hZENvbnRlbnQ8L2tleT4KICAgIDxhcnJheT4KICAgICAgICA8ZGljdD4KICAgICAgICAgICAgPGtleT5QYXlsb2FkQ29udGVudDwva2V5PgogICAgICAgICAgICA8ZGljdD4KICAgICAgICAgICAgICAgIDxrZXk+Y29tLmFwcGxlLlNldHVwQXNzaXN0YW50PC9rZXk+CiAgICAgICAgICAgICAgICA8ZGljdD4KICAgICAgICAgICAgICAgICAgICA8a2V5PlNldC1PbmNlPC9rZXk+CiAgICAgICAgICAgICAgICAgICAgPGFycmF5PgogICAgICAgICAgICAgICAgICAgICAgICA8ZGljdD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxrZXk+bWN4X2RhdGFfdGltZXN0YW1wPC9rZXk+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGF0ZT4yMDE0LTEwLTI5VDE3OjIwOjEwWjwvZGF0ZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxrZXk+bWN4X3ByZWZlcmVuY2Vfc2V0dGluZ3M8L2tleT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxkaWN0PgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxrZXk+RGlkU2VlQ2xvdWRTZXR1cDwva2V5PgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0cnVlLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8a2V5Pkdlc3R1cmVNb3ZpZVNlZW48L2tleT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c3RyaW5nPm5vbmU8L3N0cmluZz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8a2V5Pkxhc3RTZWVuQ2xvdWRQcm9kdWN0VmVyc2lvbjwva2V5PgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzdHJpbmc+MTAuMTEuMjwvc3RyaW5nPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxrZXk+TGFzdFNlZW5CdWRkeUJ1aWxkVmVyc2lvbjwva2V5PgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzdHJpbmc+MTVDNTA8L3N0cmluZz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZGljdD4KICAgICAgICAgICAgICAgICAgICAgICAgPC9kaWN0PgogICAgICAgICAgICAgICAgICAgIDwvYXJyYXk+CiAgICAgICAgICAgICAgICA8L2RpY3Q+CiAgICAgICAgICAgIDwvZGljdD4KICAgICAgICAgICAgPGtleT5QYXlsb2FkRW5hYmxlZDwva2V5PgogICAgICAgICAgICA8dHJ1ZS8+CiAgICAgICAgICAgIDxrZXk+UGF5bG9hZElkZW50aWZpZXI8L2tleT4KICAgICAgICAgICAgPHN0cmluZz5lZHUucHJhdHQuc3VwcHJlc3NfaWNsb3VkX2Fzc3Q8L3N0cmluZz4KICAgICAgICAgICAgPGtleT5QYXlsb2FkVHlwZTwva2V5PgogICAgICAgICAgICA8c3RyaW5nPmNvbS5hcHBsZS5NYW5hZ2VkQ2xpZW50LnByZWZlcmVuY2VzPC9zdHJpbmc+CiAgICAgICAgICAgIDxrZXk+UGF5bG9hZFVVSUQ8L2tleT4KICAgICAgICAgICAgPHN0cmluZz41ZTEwZjM0OC05MjNiLTQzOGEtOWI4Ny1mYTk3OGU4NmUxMWE8L3N0cmluZz4KICAgICAgICAgICAgPGtleT5QYXlsb2FkVmVyc2lvbjwva2V5PgogICAgICAgICAgICA8aW50ZWdlcj4xPC9pbnRlZ2VyPgogICAgICAgIDwvZGljdD4KICAgIDwvYXJyYXk+CiAgICA8a2V5PlBheWxvYWREZXNjcmlwdGlvbjwva2V5PgogICAgPHN0cmluZz5Db25maWd1cmVzIGNvbS5hcHBsZS5TZXR1cEFzc2lzdGFudDwvc3RyaW5nPgogICAgPGtleT5QYXlsb2FkRGlzcGxheU5hbWU8L2tleT4KICAgIDxzdHJpbmc+aUNsb3VkIFNldHVwQXNzaXN0YW50IENvbmZpZ3VyYXRpb248L3N0cmluZz4KICAgIDxrZXk+UGF5bG9hZElkZW50aWZpZXI8L2tleT4KICAgIDxzdHJpbmc+Y29tLmdpdGh1Yi5ncmVnbmVhZ2xlLnN1cHByZXNzX2ljbG91ZF9hc3N0PC9zdHJpbmc+CiAgICA8a2V5PlBheWxvYWRPcmdhbml6YXRpb248L2tleT4KICAgIDxzdHJpbmc+PC9zdHJpbmc+CiAgICA8a2V5PlBheWxvYWRSZW1vdmFsRGlzYWxsb3dlZDwva2V5PgogICAgPGZhbHNlLz4KICAgIDxrZXk+UGF5bG9hZFNjb3BlPC9rZXk+CiAgICA8c3RyaW5nPlN5c3RlbTwvc3RyaW5nPgogICAgPGtleT5QYXlsb2FkVHlwZTwva2V5PgogICAgPHN0cmluZz5Db25maWd1cmF0aW9uPC9zdHJpbmc+CiAgICA8a2V5PlBheWxvYWRVVUlEPC9rZXk+CiAgICA8c3RyaW5nPmU4MWY1MWMyLTExODAtNGRlMC05NGNkLTMxNTNhYTQxMzg3Njwvc3RyaW5nPgogICAgPGtleT5QYXlsb2FkVmVyc2lvbjwva2V5PgogICAgPGludGVnZXI+MTwvaW50ZWdlcj4KPC9kaWN0Pgo8L3BsaXN0Pgo=`, - } - return reflect.ValueOf(randomProfile) -} - -func RandomString(strlen int) string { - rand.Seed(time.Now().UTC().UnixNano()) - const chars = "abcdefghijklmnopqrstuvwxyz0123456789" - result := make([]byte, strlen) - for i := 0; i < strlen; i++ { - result[i] = chars[rand.Intn(len(chars))] - } - return string(result) -} - -var ( - testConn = "user=micromdm password=micromdm dbname=micromdm sslmode=disable" -) - -func datastore(t *testing.T) Datastore { - logger := log.NewLogfmtLogger(os.Stderr) - ds, err := NewDB("postgres", testConn, logger) - if err != nil { - t.Fatal(err) - } - return ds -} - -func setup() { - db, err := sqlx.Open("postgres", testConn) - if err != nil { - panic(err) - } - migrate(db) - defer db.Close() -} - -func teardown() { - db, err := sqlx.Open("postgres", testConn) - if err != nil { - panic(err) - } - - drop := ` - DROP TABLE IF EXISTS profiles; - ` - db.MustExec(drop) - defer db.Close() -} diff --git a/workflow/datastore.go b/workflow/datastore.go index 8a4cf613..dd0403c0 100644 --- a/workflow/datastore.go +++ b/workflow/datastore.go @@ -1,9 +1,7 @@ package workflow import ( - "database/sql" "fmt" - "strings" "time" kitlog "github.com/go-kit/kit/log" @@ -12,113 +10,32 @@ import ( "github.com/pkg/errors" ) -// ErrExists is returned if a workflow already exists -var ErrExists = errors.New("workflow already exists. each workflow must have a unique name") - -// Profile is configuration profile in a workflow -type Profile struct { - UUID string -} - -// Workflow describes a workflow that a device will execute -// A workflow contains a list of configuration profiles, -// Applications and included workflows -type Workflow struct { - UUID string `json:"uuid" db:"workflow_uuid"` - Name string `json:"name" db:"name"` - Profiles []Profile `json:"profiles"` - // Applications []application - // IncludedWorkflows []Workflow -} - // Datastore manages interactions of workflows in a database type Datastore interface { // Create adds a new workflow to the datastore - Create(wf *Workflow) (*Workflow, error) + CreateWorkflow(wf *Workflow) (*Workflow, error) + Workflows(params ...interface{}) ([]Workflow, error) + + // CreateProfile adds a new profile to the datastore, + // If a profile already exists, an error will be returned + CreateProfile(pr *Profile) (*Profile, error) + + // Profiles can query the datastore for one or more profiles + // and accepts one or more params as filters + // Example: Profiles(Identifier{"com.example.id")} + Profiles(params ...interface{}) ([]Profile, error) } type pgStore struct { *sqlx.DB } -// Create stores a new workflow in Postgres -func (store pgStore) Create(wf *Workflow) (*Workflow, error) { - err := store.QueryRow(createWorkflowStmt, wf.Name).Scan(&wf.UUID) - if err == sql.ErrNoRows { - return nil, ErrExists - } - if err != nil { - return nil, errors.Wrap(err, "pgStore create workflow") - } - - profiles := wf.Profiles - if err := store.addProfiles(wf.UUID, profiles...); err != nil { - return nil, err - } - - return wf, nil -} - -func (store pgStore) addProfiles(wfUUID string, profiles ...Profile) error { - if len(profiles) == 0 { - return nil - } - - for _, prf := range profiles { - if err := store.addProfile(wfUUID, prf.UUID); err != nil { - return err - } - } - return nil -} - -func (store pgStore) addProfile(wfUUID, pfUUID string) error { - addProfileStmt := `INSERT INTO workflow_profile (workflow_uuid, profile_uuid) VALUES ($1, $2) - ON CONFLICT ON CONSTRAINT workflow_profile_pkey DO NOTHING;` - - _, err := store.Exec(addProfileStmt, wfUUID, pfUUID) - if err != nil { - return errors.Wrap(err, "pgStore add profile to workflow") - } - return nil -} - -func (store pgStore) Workflows(params ...interface{}) ([]Workflow, error) { - stmt := selectWorkflowsStmt - var where []string - for _, param := range params { - if f, ok := param.(whereer); ok { - where = append(where, f.where()) - } - } - - if len(where) != 0 { - whereFilter := strings.Join(where, ",") - stmt = fmt.Sprintf("%s WHERE %s", selectWorkflowsStmt, whereFilter) - } - - var workflows []Workflow - err := store.Select(&workflows, stmt) - if err != nil { - return nil, errors.Wrap(err, "pgStore Workflows") - } - return workflows, nil -} - -// whereer is for building args passed into Profiles() +// whereer is for building args passed into a method which finds resources type whereer interface { where() string } -// sql statements -var ( - createWorkflowStmt = `INSERT INTO workflows (name) VALUES ($1) - ON CONFLICT ON CONSTRAINT workflows_name_key DO NOTHING - RETURNING workflow_uuid;` - selectWorkflowsStmt = `SELECT workflow_uuid, name FROM profiles` -) - //NewDB creates a Datastore func NewDB(driver, conn string, logger kitlog.Logger) (Datastore, error) { switch driver { diff --git a/profile/profile.go b/workflow/profile.go similarity index 94% rename from profile/profile.go rename to workflow/profile.go index 09f73e47..5d19ebae 100644 --- a/profile/profile.go +++ b/workflow/profile.go @@ -1,4 +1,4 @@ -package profile +package workflow // Profile is an Apple Configuration profile type Profile struct { diff --git a/workflow/profile_store.go b/workflow/profile_store.go new file mode 100644 index 00000000..76684c3e --- /dev/null +++ b/workflow/profile_store.go @@ -0,0 +1,52 @@ +package workflow + +import ( + "database/sql" + "fmt" + "strings" + + "github.com/pkg/errors" +) + +// sql statements +var ( + addProfileStmt = `INSERT INTO profiles (identifier, profile_data) VALUES ($1, $2) + ON CONFLICT ON CONSTRAINT profiles_identifier_key DO NOTHING + RETURNING profile_uuid;` + selectProfilesStmt = `SELECT profile_uuid, identifier FROM profiles` +) + +func (store pgStore) CreateProfile(prf *Profile) (*Profile, error) { + err := store.QueryRow(addProfileStmt, prf.PayloadIdentifier, prf.ProfileData).Scan(&prf.UUID) + if err == sql.ErrNoRows { + return nil, ErrExists + } + if err != nil { + return nil, errors.Wrap(err, "pgStore add profile") + } + return prf, nil +} + +func (store pgStore) Profiles(params ...interface{}) ([]Profile, error) { + stmt := selectProfilesStmt + var where []string + for _, param := range params { + if f, ok := param.(whereer); ok { + where = append(where, f.where()) + } + } + + if len(where) != 0 { + whereFilter := strings.Join(where, ",") + stmt = fmt.Sprintf("%s WHERE %s", selectProfilesStmt, whereFilter) + } + + var profiles []Profile + err := store.Select(&profiles, stmt) + if err != nil { + return nil, errors.Wrap(err, "pgStore Profiles") + } + + return profiles, nil + +} diff --git a/workflow/profile_store_test.go b/workflow/profile_store_test.go new file mode 100644 index 00000000..0e59ea27 --- /dev/null +++ b/workflow/profile_store_test.go @@ -0,0 +1 @@ +package workflow diff --git a/workflow/workflow.go b/workflow/workflow.go new file mode 100644 index 00000000..54500ca2 --- /dev/null +++ b/workflow/workflow.go @@ -0,0 +1,17 @@ +package workflow + +import "errors" + +// ErrExists is returned if a workflow already exists +var ErrExists = errors.New("workflow already exists. each workflow must have a unique name") + +// Workflow describes a workflow that a device will execute +// A workflow contains a list of configuration profiles, +// Applications and included workflows +type Workflow struct { + UUID string `json:"uuid" db:"workflow_uuid"` + Name string `json:"name" db:"name"` + Profiles []Profile `json:"profiles"` + // Applications []application + // IncludedWorkflows []Workflow +} diff --git a/workflow/workflow_store.go b/workflow/workflow_store.go new file mode 100644 index 00000000..1c580a87 --- /dev/null +++ b/workflow/workflow_store.go @@ -0,0 +1,81 @@ +package workflow + +import ( + "database/sql" + "fmt" + "strings" + + "github.com/pkg/errors" +) + +// sql statements +var ( + createWorkflowStmt = `INSERT INTO workflows (name) VALUES ($1) + ON CONFLICT ON CONSTRAINT workflows_name_key DO NOTHING + RETURNING workflow_uuid;` + selectWorkflowsStmt = `SELECT workflow_uuid, name FROM profiles` +) + +// Create stores a new workflow in Postgres +func (store pgStore) CreateWorkflow(wf *Workflow) (*Workflow, error) { + err := store.QueryRow(createWorkflowStmt, wf.Name).Scan(&wf.UUID) + if err == sql.ErrNoRows { + return nil, ErrExists + } + if err != nil { + return nil, errors.Wrap(err, "pgStore create workflow") + } + + profiles := wf.Profiles + if err := store.addProfiles(wf.UUID, profiles...); err != nil { + return nil, err + } + + return wf, nil +} + +func (store pgStore) addProfiles(wfUUID string, profiles ...Profile) error { + if len(profiles) == 0 { + return nil + } + + for _, prf := range profiles { + if err := store.addProfile(wfUUID, prf.UUID); err != nil { + return err + } + } + return nil +} + +func (store pgStore) addProfile(wfUUID, pfUUID string) error { + addProfileStmt := `INSERT INTO workflow_profile (workflow_uuid, profile_uuid) VALUES ($1, $2) + ON CONFLICT ON CONSTRAINT workflow_profile_pkey DO NOTHING;` + + _, err := store.Exec(addProfileStmt, wfUUID, pfUUID) + if err != nil { + return errors.Wrap(err, "pgStore add profile to workflow") + } + return nil +} + +func (store pgStore) Workflows(params ...interface{}) ([]Workflow, error) { + stmt := selectWorkflowsStmt + var where []string + for _, param := range params { + if f, ok := param.(whereer); ok { + where = append(where, f.where()) + } + } + + if len(where) != 0 { + whereFilter := strings.Join(where, ",") + stmt = fmt.Sprintf("%s WHERE %s", selectWorkflowsStmt, whereFilter) + } + + var workflows []Workflow + err := store.Select(&workflows, stmt) + if err != nil { + return nil, errors.Wrap(err, "pgStore Workflows") + } + return workflows, nil +} diff --git a/workflow/datastore_test.go b/workflow/workflow_store_test.go similarity index 98% rename from workflow/datastore_test.go rename to workflow/workflow_store_test.go index 587f7de9..40bd8fce 100644 --- a/workflow/datastore_test.go +++ b/workflow/workflow_store_test.go @@ -84,7 +84,7 @@ func TestDatastoreCreate(t *testing.T) { } for _, tt := range createTests { - _, err := ds.Create(tt.in) + _, err := ds.CreateWorkflow(tt.in) if !tt.shouldErr { checkErr(err) }