Skip to content

Commit cd8185d

Browse files
add support for queries in actions
1 parent 0b17c29 commit cd8185d

File tree

5 files changed

+148
-18
lines changed

5 files changed

+148
-18
lines changed

examples/sqlserver-test.yml

Lines changed: 115 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,98 @@ connect:
1111
user: "${DB_USER}"
1212
password: "${DB_PASSWORD}"
1313

14+
actions:
15+
update_user_attributes:
16+
name: Update User Attributes
17+
description: Update the attributes of a user. Only provide the attributes you want to update.
18+
action_type:
19+
- account
20+
- account_update_profile
21+
arguments:
22+
user_id:
23+
name: User ID
24+
type: string
25+
required: true
26+
description: The ID of the user to update
27+
attrs:
28+
name: Attributes
29+
type: string_map
30+
required: true
31+
description: The updated attribute data (map of attribute names to values)
32+
attrs_update_mask:
33+
name: Attributes Update Mask
34+
type: string_list
35+
required: true
36+
description: The attributes to update (list of attribute names from attrs to actually update)
37+
vars:
38+
# Map each attribute in the attrs argument to a variable and a flag to indicate if the attribute should be updated
39+
manager_id: "'manager_id' in input.attrs ? input.attrs['manager_id'] : null"
40+
update_manager_id: "'manager_id' in input.attrs_update_mask"
41+
first_name: "'first_name' in input.attrs ? input.attrs['first_name'] : null"
42+
update_first_name: "'first_name' in input.attrs_update_mask"
43+
middle_name: "'middle_name' in input.attrs ? input.attrs['middle_name'] : null"
44+
update_middle_name: "'middle_name' in input.attrs_update_mask"
45+
last_name: "'last_name' in input.attrs ? input.attrs['last_name'] : null"
46+
update_last_name: "'last_name' in input.attrs_update_mask"
47+
job_title: "'job_title' in input.attrs ? input.attrs['job_title'] : null"
48+
update_job_title: "'job_title' in input.attrs_update_mask"
49+
department: "'department' in input.attrs ? input.attrs['department'] : null"
50+
update_department: "'department' in input.attrs_update_mask"
51+
employee_id: "'employee_id' in input.attrs ? input.attrs['employee_id'] : null"
52+
update_employee_id: "'employee_id' in input.attrs_update_mask"
53+
employee_number: "'employee_number' in input.attrs ? input.attrs['employee_number'] : null"
54+
update_employee_number: "'employee_number' in input.attrs_update_mask"
55+
# We define the whole update logic with conditional attributes using CASE statements
56+
# Note: SQL Server examples has the attributes split between Users and EmployeeData tables
57+
queries:
58+
- |
59+
-- Update Users table fields
60+
UPDATE Users
61+
SET
62+
ManagerID = CASE
63+
WHEN ?<update_manager_id> = 1 THEN CAST(?<manager_id> AS INT)
64+
ELSE ManagerID
65+
END,
66+
EmployeeID = CASE
67+
WHEN ?<update_employee_id> = 1 THEN ?<employee_id>
68+
ELSE EmployeeID
69+
END
70+
WHERE Username = ?<user_id>;
71+
- |
72+
-- Update EmployeeData table fields
73+
UPDATE ed
74+
SET
75+
attr_first_name = CASE
76+
WHEN ?<update_first_name> = 1 THEN ?<first_name>
77+
ELSE ed.attr_first_name
78+
END,
79+
attr_middle_name = CASE
80+
WHEN ?<update_middle_name> = 1 THEN ?<middle_name>
81+
ELSE ed.attr_middle_name
82+
END,
83+
attr_last_name = CASE
84+
WHEN ?<update_last_name> = 1 THEN ?<last_name>
85+
ELSE ed.attr_last_name
86+
END,
87+
JobTitle = CASE
88+
WHEN ?<update_job_title> = 1 THEN ?<job_title>
89+
ELSE ed.JobTitle
90+
END,
91+
Department = CASE
92+
WHEN ?<update_department> = 1 THEN ?<department>
93+
ELSE ed.Department
94+
END,
95+
EmployeeNumber = CASE
96+
WHEN ?<update_employee_number> = 1 THEN CAST(?<employee_number> AS INT)
97+
ELSE ed.EmployeeNumber
98+
END
99+
FROM (
100+
SELECT TOP 1 EmployeeDataID, UserID
101+
FROM EmployeeData
102+
WHERE UserID = (SELECT UserID FROM Users WHERE Username = ?<user_id>)
103+
) ed_sub
104+
INNER JOIN EmployeeData ed ON ed.EmployeeDataID = ed_sub.EmployeeDataID
105+
14106
# Definition of different resource types managed by this connector
15107
resource_types:
16108
# Configuration for "user" resources in SQL Server
@@ -40,11 +132,19 @@ resource_types:
40132
END as last_login,
41133
u.ManagerID as manager_id,
42134
m.Username as manager_username,
43-
m.Email as manager_email
135+
m.Email as manager_email,
136+
ed.attr_first_name,
137+
ed.attr_middle_name,
138+
ed.attr_last_name,
139+
ed.JobTitle as attr_job_title,
140+
ed.Department as attr_department,
141+
ed.EmployeeNumber as attr_employee_number
44142
FROM
45143
Users u
46144
LEFT JOIN
47145
Users m ON u.ManagerID = m.UserID
146+
LEFT JOIN
147+
EmployeeData ed ON u.UserID = ed.UserID
48148
ORDER BY u.UserID
49149
OFFSET ?<Offset> ROWS FETCH NEXT ?<Limit> ROWS ONLY
50150
# Pagination configuration (using offset for SQL Server)
@@ -79,6 +179,12 @@ resource_types:
79179
manager_id: ".manager_id"
80180
manager_username: ".manager_username"
81181
manager_email: ".manager_email"
182+
attr_first_name: ".attr_first_name"
183+
attr_middle_name: ".attr_middle_name"
184+
attr_last_name: ".attr_last_name"
185+
attr_job_title: ".attr_job_title"
186+
attr_department: ".attr_department"
187+
attr_employee_number: ".attr_employee_number != null ? string(.attr_employee_number) : ''"
82188

83189
# Account provisioning configuration for creating new user accounts
84190
account_provisioning:
@@ -130,9 +236,16 @@ resource_types:
130236
END as last_login,
131237
u.ManagerID as manager_id,
132238
m.Username as manager_username,
133-
m.Email as manager_email
239+
m.Email as manager_email,
240+
ed.attr_first_name,
241+
ed.attr_middle_name,
242+
ed.attr_last_name,
243+
ed.JobTitle as attr_job_title,
244+
ed.Department as attr_department,
245+
ed.EmployeeNumber as attr_employee_number
134246
FROM Users u
135247
LEFT JOIN Users m ON u.ManagerID = m.UserID
248+
LEFT JOIN EmployeeData ed ON u.UserID = ed.UserID
136249
WHERE u.Username = ?<username>
137250
# Account creation configuration
138251
create:

pkg/bsql/config.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,8 @@ type ActionConfig struct {
433433
Arguments map[string]ArgumentConfig `yaml:"arguments,omitempty" json:"arguments,omitempty" validate:"omitempty,dive"`
434434
Vars map[string]string `yaml:"vars,omitempty" json:"vars,omitempty" validate:"omitempty"`
435435
NoTransaction bool `yaml:"no_transaction,omitempty" json:"no_transaction,omitempty" validate:"omitempty"`
436-
Query string `yaml:"query" json:"query" validate:"required"`
436+
Query string `yaml:"query,omitempty" json:"query,omitempty" validate:"omitempty"`
437+
Queries []string `yaml:"queries,omitempty" json:"queries,omitempty" validate:"omitempty,dive"`
437438
// TODO: add validation?
438439
//revive:disable-next-line:line-length-limit // because it's a long field
439440
ActionType []string `yaml:"action_type,omitempty" json:"action_type,omitempty" validate:"omitempty,dive,oneof=unspecified dynamic account account_update_profile account_disable account_enable"`
@@ -449,8 +450,8 @@ type ArgumentConfig struct {
449450
}
450451

451452
func (a *ActionConfig) Validate() error {
452-
if a.Query == "" {
453-
return status.Errorf(codes.InvalidArgument, "query is required")
453+
if a.Query == "" && len(a.Queries) == 0 {
454+
return status.Errorf(codes.InvalidArgument, "query or queries is required")
454455
}
455456

456457
for _, arg := range a.Arguments {

pkg/bsql/query.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,10 +399,18 @@ func (s *SQLSyncer) normalizeValue(val any) any {
399399
// Convert boolean to string for Oracle compatibility (Oracle DECODE expects CHAR)
400400
// Only convert for Oracle to avoid breaking other databases
401401
if s.dbEngine == database.Oracle {
402+
result := "0"
402403
if v {
403-
return "1"
404+
result = "1"
404405
}
405-
return "0"
406+
return result
407+
}
408+
if s.dbEngine == database.MSSQL {
409+
result := 0
410+
if v {
411+
result = 1
412+
}
413+
return result
406414
}
407415
// For other databases, return as-is (let the driver handle it)
408416
return v

pkg/connector/action.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ func (c *Connector) RegisterActionManager(ctx context.Context) (connectorbuilder
128128
actionSchema.Arguments = append(actionSchema.Arguments, arg)
129129
}
130130

131-
if actionCfg.Query == "" {
132-
return nil, fmt.Errorf("query is required for action: %s", actionKey)
131+
if actionCfg.Query == "" && len(actionCfg.Queries) == 0 {
132+
return nil, fmt.Errorf("query or queries is required for action: %s", actionKey)
133133
}
134134

135135
cfg := actionCfg
@@ -204,7 +204,12 @@ func (c *Connector) handleQueryAction(ctx context.Context, actionKey string, act
204204
argMap[k] = v
205205
}
206206

207-
queries := []string{actionCfg.Query}
207+
var queries []string
208+
if len(actionCfg.Queries) > 0 {
209+
queries = actionCfg.Queries
210+
} else {
211+
queries = []string{actionCfg.Query}
212+
}
208213
err = sqlSyncer.RunProvisioningQueries(ctx, queries, argMap, !actionCfg.NoTransaction)
209214
if err != nil {
210215
return nil, nil, err

test/sqlserver-init.sql

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ CREATE TABLE EmployeeData (
5555
Department NVARCHAR(100),
5656
JobTitle NVARCHAR(100),
5757
HireDate DATETIME2,
58+
attr_first_name NVARCHAR(100),
59+
attr_middle_name NVARCHAR(100),
60+
attr_last_name NVARCHAR(100),
5861
FOREIGN KEY (UserID) REFERENCES Users(UserID) ON DELETE CASCADE
5962
);
6063

@@ -114,14 +117,14 @@ INSERT INTO LoginHistory (UserID, LoginTime, LoginTimeText, LoginTimeAlt, LoginS
114117
((SELECT UserID FROM Users WHERE Username = 'bob.developer'), '2025-04-18 14:30:00', '18-APR-2025 14:30:00', '18/04/2025 14:30:00', 1);
115118

116119
-- Insert sample employee data
117-
INSERT INTO EmployeeData (UserID, EmployeeID, EmployeeNumber, EmployeeCode, Department, JobTitle, HireDate) VALUES
118-
((SELECT UserID FROM Users WHERE Username = 'admin'), 'EMP001', 10001, 'E-10001', 'IT', 'System Administrator', '2025-01-01'),
119-
((SELECT UserID FROM Users WHERE Username = 'jane.doe'), 'EMP002', 10002, 'E-10002', 'HR', 'HR Specialist', '2025-01-05'),
120-
((SELECT UserID FROM Users WHERE Username = 'john.smith'), 'EMP003', 10003, 'E-10003', 'Finance', 'Financial Analyst', '2025-01-10'),
121-
((SELECT UserID FROM Users WHERE Username = 'service.acct'), 'SVC001', 20001, 'S-20001', 'IT', 'Service Account', '2025-02-01'),
122-
((SELECT UserID FROM Users WHERE Username = 'disabled.user'), 'EMP004', 10004, 'E-10004', 'Marketing', 'Marketing Coordinator', '2025-02-15'),
123-
((SELECT UserID FROM Users WHERE Username = 'alice.manager'), 'EMP005', 10005, 'E-10005', 'IT', 'IT Manager', '2025-03-01'),
124-
((SELECT UserID FROM Users WHERE Username = 'bob.developer'), 'EMP006', 10006, 'E-10006', 'IT', 'Software Developer', '2025-03-05');
120+
INSERT INTO EmployeeData (UserID, EmployeeID, EmployeeNumber, EmployeeCode, Department, JobTitle, HireDate, attr_first_name, attr_middle_name, attr_last_name) VALUES
121+
((SELECT UserID FROM Users WHERE Username = 'admin'), 'EMP001', 10001, 'E-10001', 'IT', 'System Administrator', '2025-01-01', 'Admin', NULL, 'User'),
122+
((SELECT UserID FROM Users WHERE Username = 'jane.doe'), 'EMP002', 10002, 'E-10002', 'HR', 'HR Specialist', '2025-01-05', 'Jane', 'Marie', 'Doe'),
123+
((SELECT UserID FROM Users WHERE Username = 'john.smith'), 'EMP003', 10003, 'E-10003', 'Finance', 'Financial Analyst', '2025-01-10', 'John', 'Michael', 'Smith'),
124+
((SELECT UserID FROM Users WHERE Username = 'service.acct'), 'SVC001', 20001, 'S-20001', 'IT', 'Service Account', '2025-02-01', NULL, NULL, NULL),
125+
((SELECT UserID FROM Users WHERE Username = 'disabled.user'), 'EMP004', 10004, 'E-10004', 'Marketing', 'Marketing Coordinator', '2025-02-15', 'Disabled', NULL, 'User'),
126+
((SELECT UserID FROM Users WHERE Username = 'alice.manager'), 'EMP005', 10005, 'E-10005', 'IT', 'IT Manager', '2025-03-01', 'Alice', NULL, 'Manager'),
127+
((SELECT UserID FROM Users WHERE Username = 'bob.developer'), 'EMP006', 10006, 'E-10006', 'IT', 'Software Developer', '2025-03-05', 'Bob', NULL, 'Developer');
125128

126129
-- Create indexes for better performance
127130
CREATE INDEX IX_Users_Username ON Users(Username);

0 commit comments

Comments
 (0)