1package repositories
2
3import (
4	"database/sql"
5
6	"github.com/pkg/errors"
7	"github.com/satori/go.uuid"
8
9	"repodiff/constants"
10	e "repodiff/entities"
11	"repodiff/mappers"
12	repoSQL "repodiff/persistence/sql"
13	"repodiff/utils"
14)
15
16type project struct {
17	db                 *sql.DB
18	target             e.MappedDiffTarget
19	timestampGenerator func() e.RepoTimestamp
20}
21
22func (p project) InsertDiffRows(diffRows []e.AnalyzedDiffRow) error {
23	return errors.Wrap(
24		repoSQL.SingleTransactionInsert(
25			p.db,
26			`INSERT INTO project_differential (
27				upstream_target_id,
28				downstream_target_id,
29				timestamp,
30				uuid,
31				row_index,
32				downstream_project,
33				upstream_project,
34				status,
35				files_changed,
36				line_insertions,
37				line_deletions,
38				line_changes,
39				commits_not_upstreamed,
40				project_type
41			) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
42			mappers.PrependMappedDiffTarget(
43				p.target,
44				mappers.DiffRowsToPersistCols(diffRows, p.timestampGenerator()),
45			),
46		),
47		"Error inserting rows into project_differential",
48	)
49}
50
51func (p project) GetMostRecentOuterKey() (int64, uuid.UUID, error) {
52	var timestamp int64
53	var uuidBytes []byte
54	err := p.db.QueryRow(
55		`SELECT timestamp, uuid FROM project_differential WHERE upstream_target_id = ? AND downstream_target_id = ? AND timestamp=(
56			SELECT MAX(timestamp) FROM project_differential WHERE upstream_target_id = ? AND downstream_target_id = ?
57		) LIMIT 1`,
58		p.target.UpstreamTarget,
59		p.target.DownstreamTarget,
60		p.target.UpstreamTarget,
61		p.target.DownstreamTarget,
62	).Scan(
63		&timestamp,
64		&uuidBytes,
65	)
66	if err != nil {
67		return 0, constants.NullUUID(), err
68	}
69	u, err := uuid.FromBytes(uuidBytes)
70	if err != nil {
71		return 0, constants.NullUUID(), errors.Wrap(err, "Error casting string to UUID")
72	}
73	return timestamp, u, nil
74}
75
76func (p project) GetMostRecentDifferentials() ([]e.AnalyzedDiffRow, error) {
77	timestamp, uid, err := p.GetMostRecentOuterKey()
78	if err == sql.ErrNoRows {
79		return nil, nil
80	}
81	if err != nil {
82		// TODO this doesn't handle empty case properly
83		return nil, err
84	}
85	var errMapping error
86
87	var diffRows []e.AnalyzedDiffRow
88	errSelect := repoSQL.Select(
89		p.db,
90		func(row *sql.Rows) {
91			if errMapping != nil {
92				return
93			}
94			d, err := mappers.SQLRowToDiffRow(row)
95			errMapping = err
96			diffRows = append(
97				diffRows,
98				d,
99			)
100		},
101		`SELECT
102		  timestamp,
103			uuid,
104			row_index,
105			downstream_project,
106			upstream_project,
107			status,
108			files_changed,
109			line_insertions,
110			line_deletions,
111			line_changes,
112			commits_not_upstreamed,
113			project_type
114		FROM project_differential
115		WHERE
116		  upstream_target_id = ?
117			AND downstream_target_id = ?
118			AND timestamp = ?
119			AND uuid = ?`,
120		p.target.UpstreamTarget,
121		p.target.DownstreamTarget,
122		timestamp,
123		string(uid.Bytes()),
124	)
125	if errSelect != nil {
126		return nil, errSelect
127	}
128	if errMapping != nil {
129		return nil, errMapping
130	}
131	return diffRows, nil
132}
133
134func NewProjectRepository(target e.MappedDiffTarget) (project, error) {
135	db, err := repoSQL.GetDBConnectionPool()
136	return project{
137		db:                 db,
138		target:             target,
139		timestampGenerator: utils.TimestampSeconds,
140	}, errors.Wrap(err, "Could not establish a database connection")
141}
142