<commit_before>package iamstorage

import (
    "fmt"
    "database/sql"
    "strings"

    "github.com/gosimple/slug"
    "github.com/jaypipes/sqlb"

    "github.com/jaypipes/procession/pkg/errors"
    "github.com/jaypipes/procession/pkg/sqlutil"
    "github.com/jaypipes/procession/pkg/storage"
    "github.com/jaypipes/procession/pkg/util"
    pb "github.com/jaypipes/procession/proto"
)

var (
    userSortFields = []*sqlutil.SortFieldInfo{
        &sqlutil.SortFieldInfo{
            Name: "uuid",
            Unique: true,
        },
        &sqlutil.SortFieldInfo{
            Name: "email",
            Unique: true,
        },
        &sqlutil.SortFieldInfo{
            Name: "display_name",
            Unique: false,
            Aliases: []string{
                "name",
                "display name",
                "display_name",
            },
        },
    }
)

// A generator that returns a function that returns a string value of a sort
// field after looking up an user by UUID
func (s *IAMStorage) userSortFieldByUuid(
    uuid string,
    sortField *pb.SortField,
) func () string {
    f := func () string {
        qs := fmt.Sprintf(
            "SELECT %s FROM users WHERE uuid = ?",
            sortField.Field,
        )
        qargs := []interface{}{uuid}
        rows, err := s.Rows(qs, qargs...)
        if err != nil {
            return ""
        }
        defer rows.Close()
        var sortVal string
        for rows.Next() {
            err = rows.Scan(&sortVal)
            if err != nil {
                return ""
            }
            return sortVal
        }
        return ""
    }
    return f
}

// Simple wrapper struct that allows us to pass the internal ID for a
// user around with a protobuf message of the external representation
// of the user
type userRecord struct {
    pb *pb.User
    id int64
}

// Returns a sql.Rows yielding users matching a set of supplied filters
func (s *IAMStorage) UserList(
    req *pb.UserListRequest,
) (storage.RowIterator, error) {
    filters := req.Filters
    opts := req.Options
    err := sqlutil.NormalizeSortFields(opts, &userSortFields)
    if err != nil {
        return nil, err
    }
    qs := `
SELECT
  u.uuid
, u.email
, u.display_name
, u.slug
, u.generation
FROM users AS u
`
    qargs := make([]interface{}, 0)
    if filters.Identifiers != nil {
        qs = qs + "WHERE ("
        for x, search := range filters.Identifiers {
            orStr := ""
            if x > 0 {
                orStr = "\nOR "
            }
            colName := "u.slug"
            if util.IsUuidLike(search) {
                colName = "u.uuid"
                search = util.UuidFormatDb(search)
            } else if util.IsEmailLike(search) {
                colName = "u.email"
            } else {
                search = slug.Make(search)
            }
            qs = qs + fmt.Sprintf(
                "%s%s = ?",
                orStr,
                colName,
            )
            qargs = append(qargs, search)
        }
        qs = qs + ")"
    }
    // if qargs has nothing in it, a WHERE clause has not yet been added...
    if len(qargs) == 0 && opts.Marker != "" {
        qs = qs + "WHERE "
    }
    if opts.Marker != "" && len(opts.SortFields) > 0 {
        sqlutil.AddMarkerWhere(
            &qs,
            opts,
            "u",
            (len(qargs) > 0),
            &qargs,
            orgSortFields,
            s.orgSortFieldByUuid(opts.Marker, opts.SortFields[0]),
        )
    }

    sqlutil.AddOrderBy(&qs, opts, "u")
    qs = qs + "\nLIMIT ?"
    qargs = append(qargs, opts.Limit)

    rows, err := s.Rows(qs, qargs...)
    if err != nil {
        return nil, err
    }
    return rows, nil
}

// Returns a list of user IDs for users belonging to an entire organization
// tree excluding a supplied user ID
func (s *IAMStorage) usersInOrgTreeExcluding(
    rootOrgId uint64,
    excludeUserId uint64,
) ([]uint64, error) {
    m := s.Meta()
    outbl := m.TableDef("organization_users").As("ou")
    otbl := m.TableDef("organizations").As("o")
    colOUUserId := outbl.Column("user_id")
    colOUOrgId := outbl.Column("organization_id")
    colOrgId := otbl.Column("id")
    colRootOrgId := otbl.Column("root_organization_id")
    joinCond := sqlb.Equal(colOUOrgId, colOrgId)
    q := sqlb.Select(colOUUserId).Join(otbl, joinCond)
    q.Where(
        sqlb.And(
            sqlb.Equal(colRootOrgId, rootOrgId),
            sqlb.NotEqual(colOUUserId, excludeUserId),
        ),
    )
    qs, qargs := q.StringArgs()
    out := make([]uint64, 0)
    rows, err := s.Rows(qs, qargs...)
    if err != nil {
        return nil, err
    }
    defer rows.Close()
    for rows.Next() {
        var userId uint64
        err = rows.Scan(&userId)
        if err != nil {
            return nil, err
        }
        out = append(out, userId)
    }
    return out, nil
}

// Returns a list of user IDs for users belonging to one specific organization
// (not the entire tree) excluding a supplied user ID
func (s *IAMStorage) usersInOrgExcluding(
    orgId uint64,
    excludeUserId uint64,
) ([]uint64, error) {
    qs := `
SELECT ou.user_id
FROM organization_users AS ou
WHERE ou.organization_id = ?
AND ou.user_id != ?
`
    out := make([]uint64, 0)
    rows, err := s.Rows(qs, orgId, excludeUserId)
    if err != nil {
        return nil, err
    }
    defer rows.Close()
    for rows.Next() {
        var userId uint64
        err = rows.Scan(&userId)
        if err != nil {
            return nil, err
        }
        out = append(out, userId)<commit_msg>Update usersInOrgExcluding() to use sqlb<commit_after>package iamstorage

import (
    "fmt"
    "database/sql"
    "strings"

    "github.com/gosimple/slug"
    "github.com/jaypipes/sqlb"

    "github.com/jaypipes/procession/pkg/errors"
    "github.com/jaypipes/procession/pkg/sqlutil"
    "github.com/jaypipes/procession/pkg/storage"
    "github.com/jaypipes/procession/pkg/util"
    pb "github.com/jaypipes/procession/proto"
)

var (
    userSortFields = []*sqlutil.SortFieldInfo{
        &sqlutil.SortFieldInfo{
            Name: "uuid",
            Unique: true,
        },
        &sqlutil.SortFieldInfo{
            Name: "email",
            Unique: true,
        },
        &sqlutil.SortFieldInfo{
            Name: "display_name",
            Unique: false,
            Aliases: []string{
                "name",
                "display name",
                "display_name",
            },
        },
    }
)

// A generator that returns a function that returns a string value of a sort
// field after looking up an user by UUID
func (s *IAMStorage) userSortFieldByUuid(
    uuid string,
    sortField *pb.SortField,
) func () string {
    f := func () string {
        qs := fmt.Sprintf(
            "SELECT %s FROM users WHERE uuid = ?",
            sortField.Field,
        )
        qargs := []interface{}{uuid}
        rows, err := s.Rows(qs, qargs...)
        if err != nil {
            return ""
        }
        defer rows.Close()
        var sortVal string
        for rows.Next() {
            err = rows.Scan(&sortVal)
            if err != nil {
                return ""
            }
            return sortVal
        }
        return ""
    }
    return f
}

// Simple wrapper struct that allows us to pass the internal ID for a
// user around with a protobuf message of the external representation
// of the user
type userRecord struct {
    pb *pb.User
    id int64
}

// Returns a sql.Rows yielding users matching a set of supplied filters
func (s *IAMStorage) UserList(
    req *pb.UserListRequest,
) (storage.RowIterator, error) {
    filters := req.Filters
    opts := req.Options
    err := sqlutil.NormalizeSortFields(opts, &userSortFields)
    if err != nil {
        return nil, err
    }
    qs := `
SELECT
  u.uuid
, u.email
, u.display_name
, u.slug
, u.generation
FROM users AS u
`
    qargs := make([]interface{}, 0)
    if filters.Identifiers != nil {
        qs = qs + "WHERE ("
        for x, search := range filters.Identifiers {
            orStr := ""
            if x > 0 {
                orStr = "\nOR "
            }
            colName := "u.slug"
            if util.IsUuidLike(search) {
                colName = "u.uuid"
                search = util.UuidFormatDb(search)
            } else if util.IsEmailLike(search) {
                colName = "u.email"
            } else {
                search = slug.Make(search)
            }
            qs = qs + fmt.Sprintf(
                "%s%s = ?",
                orStr,
                colName,
            )
            qargs = append(qargs, search)
        }
        qs = qs + ")"
    }
    // if qargs has nothing in it, a WHERE clause has not yet been added...
    if len(qargs) == 0 && opts.Marker != "" {
        qs = qs + "WHERE "
    }
    if opts.Marker != "" && len(opts.SortFields) > 0 {
        sqlutil.AddMarkerWhere(
            &qs,
            opts,
            "u",
            (len(qargs) > 0),
            &qargs,
            orgSortFields,
            s.orgSortFieldByUuid(opts.Marker, opts.SortFields[0]),
        )
    }

    sqlutil.AddOrderBy(&qs, opts, "u")
    qs = qs + "\nLIMIT ?"
    qargs = append(qargs, opts.Limit)

    rows, err := s.Rows(qs, qargs...)
    if err != nil {
        return nil, err
    }
    return rows, nil
}

// Returns a list of user IDs for users belonging to an entire organization
// tree excluding a supplied user ID
func (s *IAMStorage) usersInOrgTreeExcluding(
    rootOrgId uint64,
    excludeUserId uint64,
) ([]uint64, error) {
    m := s.Meta()
    outbl := m.TableDef("organization_users").As("ou")
    otbl := m.TableDef("organizations").As("o")
    colOUUserId := outbl.Column("user_id")
    colOUOrgId := outbl.Column("organization_id")
    colOrgId := otbl.Column("id")
    colRootOrgId := otbl.Column("root_organization_id")
    joinCond := sqlb.Equal(colOUOrgId, colOrgId)
    q := sqlb.Select(colOUUserId).Join(otbl, joinCond)
    q.Where(
        sqlb.And(
            sqlb.Equal(colRootOrgId, rootOrgId),
            sqlb.NotEqual(colOUUserId, excludeUserId),
        ),
    )
    qs, qargs := q.StringArgs()
    out := make([]uint64, 0)
    rows, err := s.Rows(qs, qargs...)
    if err != nil {
        return nil, err
    }
    defer rows.Close()
    for rows.Next() {
        var userId uint64
        err = rows.Scan(&userId)
        if err != nil {
            return nil, err
        }
        out = append(out, userId)
    }
    return out, nil
}

// Returns a list of user IDs for users belonging to one specific organization
// (not the entire tree) excluding a supplied user ID
func (s *IAMStorage) usersInOrgExcluding(
    orgId uint64,
    excludeUserId uint64,
) ([]uint64, error) {
    m := s.Meta()
    outbl := m.TableDef("organization_users").As("ou")
    colOUUserId := outbl.Column("user_id")
    colOUOrgId := outbl.Column("organization_id")
    q := sqlb.Select(colOUUserId)
    q.Where(
        sqlb.And(
            sqlb.Equal(colOUOrgId, orgId),
            sqlb.NotEqual(colOUUserId, excludeUserId),
        ),
    )
    qs, qargs := q.StringArgs()
    out := make([]uint64, 0)
    rows, err := s.Rows(qs, qargs...)
    if err != nil {
        return nil, err
    }
    defer rows.Close()
    for rows.Next() {
        var userId uint64
        err = rows.Scan(&userId)
        if err != nil {
            return nil, err
        }
        out = append(out, userId)
{'fraction_non_alphanumeric': 0.12170650846274647, 'fraction_numerical': 0.005234688536032106, 'mean_word_length': 1.7675036214389184, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1330443', 'n_tokens_mistral': 3721, 'n_tokens_neox': 3569, 'n_words': 924}