-
Notifications
You must be signed in to change notification settings - Fork 22
/
resource.go
139 lines (122 loc) · 3.41 KB
/
resource.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package schema
import (
"fmt"
"github.com/google/uuid"
)
type Resources []*Resource
// Resource represents a row in it's associated table, it carries a reference to the original item, and automatically
// generates an Id based on Table's Columns. Resource data can be accessed by the Get and Set methods
type Resource struct {
// Original resource item that wa from prior resolve
Item any
// Set if this is an embedded table
Parent *Resource
// internal fields
Table *Table
// This is sorted result data by column name
data CQTypes
}
// This struct is what we send over the wire to destination.
// We dont want to reuse the same struct as otherwise we will have to comment on fields which don't get sent over the wire but still accessible
// code wise
type DestinationResource struct {
TableName string `json:"table_name"`
Data CQTypes `json:"data"`
}
func NewResourceData(t *Table, parent *Resource, item any) *Resource {
r := Resource{
Item: item,
Parent: parent,
Table: t,
data: make(CQTypes, len(t.Columns)),
}
for i := range r.data {
r.data[i] = NewCqTypeFromValueType(t.Columns[i].Type)
}
return &r
}
func (r *Resource) ToDestinationResource() DestinationResource {
dr := DestinationResource{
TableName: r.Table.Name,
Data: r.data,
}
return dr
}
func (r *Resource) Get(columnName string) CQType {
index := r.Table.Columns.Index(columnName)
if index == -1 {
// we panic because we want to distinguish between code error and api error
// this also saves additional checks in our testing code
panic(columnName + " column not found")
}
return r.data[index]
}
// Set sets a column with value. This does validation and conversion to
// one of concrete it returns an error just for backward compatibility
// and panics in case it fails
func (r *Resource) Set(columnName string, value any) error {
index := r.Table.Columns.Index(columnName)
if index == -1 {
// we panic because we want to distinguish between code error and api error
// this also saves additional checks in our testing code
panic(columnName + " column not found")
}
if err := r.data[index].Set(value); err != nil {
panic(fmt.Errorf("failed to set column %s: %w", columnName, err))
}
return nil
}
// Override original item (this is useful for apis that follow list/details pattern)
func (r *Resource) SetItem(item any) {
r.Item = item
}
func (r *Resource) GetItem() any {
return r.Item
}
func (r *Resource) GetValues() CQTypes {
return r.data
}
func (r *Resource) ID() uuid.UUID {
index := r.Table.Columns.Index(CqIDColumn.Name)
if index == -1 {
return uuid.UUID{}
}
return uuid.UUID{}
}
func (r *Resource) Columns() []string {
return r.Table.Columns.Names()
}
// Validates that all primary keys have values.
func (r *Resource) Validate() error {
var missingPks []string
for i, c := range r.Table.Columns {
if c.CreationOptions.PrimaryKey {
if r.data[i].GetStatus() != Present {
missingPks = append(missingPks, c.Name)
}
}
}
if len(missingPks) > 0 {
return fmt.Errorf("missing primary key on columns: %v", missingPks)
}
return nil
}
func (rr Resources) GetIds() []uuid.UUID {
rids := make([]uuid.UUID, len(rr))
for i, r := range rr {
rids[i] = r.ID()
}
return rids
}
func (rr Resources) TableName() string {
if len(rr) == 0 {
return ""
}
return rr[0].Table.Name
}
func (rr Resources) ColumnNames() []string {
if len(rr) == 0 {
return []string{}
}
return rr[0].Table.Columns.Names()
}