mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: add user details to aibridge interception list endpoint (#20397)
- Adds FK from `aibridge_interceptions.initiator_id` to `users.id`
- This is enforced by deleting any rows that don't have any users. Since
this is an experimental feature AND coder never deletes user rows I
think this is acceptable.
- Adds `name` as a property on `codersdk.MinimalUser`
- This matches the `visible_users` view in the database. I'm unsure why
`name` wasn't already included given that `username` is.
- Adds a new `initiator` field to `codersdk.AIBridgeInterception` which
contains `codersdk.MinimalUser` (ID, username, name, avatar URL)
- Removes `initiator_id` from `codersdk.AIBridgeInterception`
- Should be fine since we're still in early access
This commit is contained in:
@@ -189,6 +189,16 @@ func MinimalUser(user database.User) codersdk.MinimalUser {
|
||||
return codersdk.MinimalUser{
|
||||
ID: user.ID,
|
||||
Username: user.Username,
|
||||
Name: user.Name,
|
||||
AvatarURL: user.AvatarURL,
|
||||
}
|
||||
}
|
||||
|
||||
func MinimalUserFromVisibleUser(user database.VisibleUser) codersdk.MinimalUser {
|
||||
return codersdk.MinimalUser{
|
||||
ID: user.ID,
|
||||
Username: user.Username,
|
||||
Name: user.Name,
|
||||
AvatarURL: user.AvatarURL,
|
||||
}
|
||||
}
|
||||
@@ -197,7 +207,6 @@ func ReducedUser(user database.User) codersdk.ReducedUser {
|
||||
return codersdk.ReducedUser{
|
||||
MinimalUser: MinimalUser(user),
|
||||
Email: user.Email,
|
||||
Name: user.Name,
|
||||
CreatedAt: user.CreatedAt,
|
||||
UpdatedAt: user.UpdatedAt,
|
||||
LastSeenAt: user.LastSeenAt,
|
||||
@@ -927,7 +936,7 @@ func PreviewParameterValidation(v *previewtypes.ParameterValidation) codersdk.Pr
|
||||
}
|
||||
}
|
||||
|
||||
func AIBridgeInterception(interception database.AIBridgeInterception, tokenUsages []database.AIBridgeTokenUsage, userPrompts []database.AIBridgeUserPrompt, toolUsages []database.AIBridgeToolUsage) codersdk.AIBridgeInterception {
|
||||
func AIBridgeInterception(interception database.AIBridgeInterception, initiator database.VisibleUser, tokenUsages []database.AIBridgeTokenUsage, userPrompts []database.AIBridgeUserPrompt, toolUsages []database.AIBridgeToolUsage) codersdk.AIBridgeInterception {
|
||||
sdkTokenUsages := List(tokenUsages, AIBridgeTokenUsage)
|
||||
sort.Slice(sdkTokenUsages, func(i, j int) bool {
|
||||
// created_at ASC
|
||||
@@ -945,7 +954,7 @@ func AIBridgeInterception(interception database.AIBridgeInterception, tokenUsage
|
||||
})
|
||||
return codersdk.AIBridgeInterception{
|
||||
ID: interception.ID,
|
||||
InitiatorID: interception.InitiatorID,
|
||||
Initiator: MinimalUserFromVisibleUser(initiator),
|
||||
Provider: interception.Provider,
|
||||
Model: interception.Model,
|
||||
Metadata: jsonOrEmptyMap(interception.Metadata),
|
||||
|
||||
@@ -4461,7 +4461,7 @@ func (q *querier) InsertWorkspaceResourceMetadata(ctx context.Context, arg datab
|
||||
return q.db.InsertWorkspaceResourceMetadata(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) ListAIBridgeInterceptions(ctx context.Context, arg database.ListAIBridgeInterceptionsParams) ([]database.AIBridgeInterception, error) {
|
||||
func (q *querier) ListAIBridgeInterceptions(ctx context.Context, arg database.ListAIBridgeInterceptionsParams) ([]database.ListAIBridgeInterceptionsRow, error) {
|
||||
prep, err := prepareSQLFilter(ctx, q.auth, policy.ActionRead, rbac.ResourceAibridgeInterception.Type)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("(dev error) prepare sql filter: %w", err)
|
||||
@@ -5870,7 +5870,7 @@ func (q *querier) CountAuthorizedConnectionLogs(ctx context.Context, arg databas
|
||||
return q.CountConnectionLogs(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) ListAuthorizedAIBridgeInterceptions(ctx context.Context, arg database.ListAIBridgeInterceptionsParams, _ rbac.PreparedAuthorized) ([]database.AIBridgeInterception, error) {
|
||||
func (q *querier) ListAuthorizedAIBridgeInterceptions(ctx context.Context, arg database.ListAIBridgeInterceptionsParams, _ rbac.PreparedAuthorized) ([]database.ListAIBridgeInterceptionsRow, error) {
|
||||
// TODO: Delete this function, all ListAIBridgeInterceptions should be authorized. For now just call ListAIBridgeInterceptions on the authz querier.
|
||||
// This cannot be deleted for now because it's included in the
|
||||
// database.Store interface, so dbauthz needs to implement it.
|
||||
|
||||
@@ -4537,14 +4537,14 @@ func (s *MethodTestSuite) TestAIBridge() {
|
||||
|
||||
s.Run("ListAIBridgeInterceptions", s.Mocked(func(db *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
|
||||
params := database.ListAIBridgeInterceptionsParams{}
|
||||
db.EXPECT().ListAuthorizedAIBridgeInterceptions(gomock.Any(), params, gomock.Any()).Return([]database.AIBridgeInterception{}, nil).AnyTimes()
|
||||
db.EXPECT().ListAuthorizedAIBridgeInterceptions(gomock.Any(), params, gomock.Any()).Return([]database.ListAIBridgeInterceptionsRow{}, nil).AnyTimes()
|
||||
// No asserts here because SQLFilter.
|
||||
check.Args(params).Asserts()
|
||||
}))
|
||||
|
||||
s.Run("ListAuthorizedAIBridgeInterceptions", s.Mocked(func(db *dbmock.MockStore, faker *gofakeit.Faker, check *expects) {
|
||||
params := database.ListAIBridgeInterceptionsParams{}
|
||||
db.EXPECT().ListAuthorizedAIBridgeInterceptions(gomock.Any(), params, gomock.Any()).Return([]database.AIBridgeInterception{}, nil).AnyTimes()
|
||||
db.EXPECT().ListAuthorizedAIBridgeInterceptions(gomock.Any(), params, gomock.Any()).Return([]database.ListAIBridgeInterceptionsRow{}, nil).AnyTimes()
|
||||
// No asserts here because SQLFilter.
|
||||
check.Args(params, emptyPreparedAuthorized{}).Asserts()
|
||||
}))
|
||||
|
||||
@@ -2714,7 +2714,7 @@ func (m queryMetricsStore) InsertWorkspaceResourceMetadata(ctx context.Context,
|
||||
return metadata, err
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) ListAIBridgeInterceptions(ctx context.Context, arg database.ListAIBridgeInterceptionsParams) ([]database.AIBridgeInterception, error) {
|
||||
func (m queryMetricsStore) ListAIBridgeInterceptions(ctx context.Context, arg database.ListAIBridgeInterceptionsParams) ([]database.ListAIBridgeInterceptionsRow, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.ListAIBridgeInterceptions(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("ListAIBridgeInterceptions").Observe(time.Since(start).Seconds())
|
||||
@@ -3722,7 +3722,7 @@ func (m queryMetricsStore) CountAuthorizedConnectionLogs(ctx context.Context, ar
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) ListAuthorizedAIBridgeInterceptions(ctx context.Context, arg database.ListAIBridgeInterceptionsParams, prepared rbac.PreparedAuthorized) ([]database.AIBridgeInterception, error) {
|
||||
func (m queryMetricsStore) ListAuthorizedAIBridgeInterceptions(ctx context.Context, arg database.ListAIBridgeInterceptionsParams, prepared rbac.PreparedAuthorized) ([]database.ListAIBridgeInterceptionsRow, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.ListAuthorizedAIBridgeInterceptions(ctx, arg, prepared)
|
||||
m.queryLatencies.WithLabelValues("ListAuthorizedAIBridgeInterceptions").Observe(time.Since(start).Seconds())
|
||||
|
||||
@@ -5804,10 +5804,10 @@ func (mr *MockStoreMockRecorder) InsertWorkspaceResourceMetadata(ctx, arg any) *
|
||||
}
|
||||
|
||||
// ListAIBridgeInterceptions mocks base method.
|
||||
func (m *MockStore) ListAIBridgeInterceptions(ctx context.Context, arg database.ListAIBridgeInterceptionsParams) ([]database.AIBridgeInterception, error) {
|
||||
func (m *MockStore) ListAIBridgeInterceptions(ctx context.Context, arg database.ListAIBridgeInterceptionsParams) ([]database.ListAIBridgeInterceptionsRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ListAIBridgeInterceptions", ctx, arg)
|
||||
ret0, _ := ret[0].([]database.AIBridgeInterception)
|
||||
ret0, _ := ret[0].([]database.ListAIBridgeInterceptionsRow)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
@@ -5864,10 +5864,10 @@ func (mr *MockStoreMockRecorder) ListAIBridgeUserPromptsByInterceptionIDs(ctx, i
|
||||
}
|
||||
|
||||
// ListAuthorizedAIBridgeInterceptions mocks base method.
|
||||
func (m *MockStore) ListAuthorizedAIBridgeInterceptions(ctx context.Context, arg database.ListAIBridgeInterceptionsParams, prepared rbac.PreparedAuthorized) ([]database.AIBridgeInterception, error) {
|
||||
func (m *MockStore) ListAuthorizedAIBridgeInterceptions(ctx context.Context, arg database.ListAIBridgeInterceptionsParams, prepared rbac.PreparedAuthorized) ([]database.ListAIBridgeInterceptionsRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ListAuthorizedAIBridgeInterceptions", ctx, arg, prepared)
|
||||
ret0, _ := ret[0].([]database.AIBridgeInterception)
|
||||
ret0, _ := ret[0].([]database.ListAIBridgeInterceptionsRow)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
Generated
+3
@@ -3511,6 +3511,9 @@ COMMENT ON TRIGGER workspace_agent_name_unique_trigger ON workspace_agents IS 'U
|
||||
the uniqueness requirement. A trigger allows us to enforce uniqueness going
|
||||
forward without requiring a migration to clean up historical data.';
|
||||
|
||||
ALTER TABLE ONLY aibridge_interceptions
|
||||
ADD CONSTRAINT aibridge_interceptions_initiator_id_fkey FOREIGN KEY (initiator_id) REFERENCES users(id);
|
||||
|
||||
ALTER TABLE ONLY api_keys
|
||||
ADD CONSTRAINT api_keys_user_id_uuid_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ type ForeignKeyConstraint string
|
||||
|
||||
// ForeignKeyConstraint enums.
|
||||
const (
|
||||
ForeignKeyAibridgeInterceptionsInitiatorID ForeignKeyConstraint = "aibridge_interceptions_initiator_id_fkey" // ALTER TABLE ONLY aibridge_interceptions ADD CONSTRAINT aibridge_interceptions_initiator_id_fkey FOREIGN KEY (initiator_id) REFERENCES users(id);
|
||||
ForeignKeyAPIKeysUserIDUUID ForeignKeyConstraint = "api_keys_user_id_uuid_fkey" // ALTER TABLE ONLY api_keys ADD CONSTRAINT api_keys_user_id_uuid_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||
ForeignKeyConnectionLogsOrganizationID ForeignKeyConstraint = "connection_logs_organization_id_fkey" // ALTER TABLE ONLY connection_logs ADD CONSTRAINT connection_logs_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
|
||||
ForeignKeyConnectionLogsWorkspaceID ForeignKeyConstraint = "connection_logs_workspace_id_fkey" // ALTER TABLE ONLY connection_logs ADD CONSTRAINT connection_logs_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE;
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
-- We didn't add an FK as a premature optimization when the aibridge tables were
|
||||
-- added, but for the initiator_id it's pretty annoying not having a strong
|
||||
-- reference.
|
||||
--
|
||||
-- Since the aibridge feature is still in early access, we're going to add the
|
||||
-- FK and drop any rows that violate it (which should be none). This isn't a
|
||||
-- very efficient migration, but since the feature is behind an experimental
|
||||
-- flag, it shouldn't have any impact on deployments that aren't using the
|
||||
-- feature.
|
||||
|
||||
-- Step 1: Add FK without validating it
|
||||
ALTER TABLE aibridge_interceptions
|
||||
ADD CONSTRAINT aibridge_interceptions_initiator_id_fkey
|
||||
FOREIGN KEY (initiator_id)
|
||||
REFERENCES users(id)
|
||||
-- We can't:
|
||||
-- - Cascade delete because this is an auditing feature, and it also
|
||||
-- wouldn't delete related aibridge rows since we don't FK them.
|
||||
-- - Set null because you can't correlate to the original user ID if the
|
||||
-- user somehow gets deleted.
|
||||
--
|
||||
-- So we just use the default and don't do anything. This will result in a
|
||||
-- deferred constraint violation error when the user is deleted.
|
||||
--
|
||||
-- In Coder, we don't delete user rows ever, so this should never happen
|
||||
-- unless an admin manually deletes a user with SQL.
|
||||
ON DELETE NO ACTION
|
||||
-- Delay validation of existing data until after we've dropped rows that
|
||||
-- violate the FK.
|
||||
NOT VALID;
|
||||
|
||||
-- Step 2: Drop existing interceptions that violate the FK.
|
||||
DELETE FROM aibridge_interceptions
|
||||
WHERE initiator_id NOT IN (SELECT id FROM users);
|
||||
|
||||
-- Step 3: Drop existing rows from other tables that no longer have a valid
|
||||
-- interception in the database.
|
||||
DELETE FROM aibridge_token_usages
|
||||
WHERE interception_id NOT IN (SELECT id FROM aibridge_interceptions);
|
||||
|
||||
DELETE FROM aibridge_user_prompts
|
||||
WHERE interception_id NOT IN (SELECT id FROM aibridge_interceptions);
|
||||
|
||||
DELETE FROM aibridge_tool_usages
|
||||
WHERE interception_id NOT IN (SELECT id FROM aibridge_interceptions);
|
||||
|
||||
-- Step 4: Validate the FK
|
||||
ALTER TABLE aibridge_interceptions
|
||||
VALIDATE CONSTRAINT aibridge_interceptions_initiator_id_fkey;
|
||||
@@ -8,7 +8,7 @@ INSERT INTO
|
||||
)
|
||||
VALUES (
|
||||
'be003e1e-b38f-43bf-847d-928074dd0aa8',
|
||||
'30095c71-380b-457a-8995-97b8ee6e5307',
|
||||
'30095c71-380b-457a-8995-97b8ee6e5307', -- admin@coder.com, from 000022_initial_v0.6.6.up.sql
|
||||
'openai',
|
||||
'gpt-5',
|
||||
'2025-09-15 12:45:13.921148+00'
|
||||
@@ -77,3 +77,82 @@ VALUES (
|
||||
'{}',
|
||||
'2025-09-15 12:45:21.674335+00'
|
||||
);
|
||||
|
||||
-- For a later migration, we'll add an invalid interception without a valid
|
||||
-- initiator_id.
|
||||
INSERT INTO
|
||||
aibridge_interceptions (
|
||||
id,
|
||||
initiator_id,
|
||||
provider,
|
||||
model,
|
||||
started_at
|
||||
)
|
||||
VALUES (
|
||||
'c6d29c6e-26a3-4137-bb2e-9dfeef3c1c26',
|
||||
'cab8d56a-8922-4999-81a9-046b43ac1312', -- user does not exist
|
||||
'openai',
|
||||
'gpt-5',
|
||||
'2025-09-15 12:45:13.921148+00'
|
||||
);
|
||||
INSERT INTO
|
||||
aibridge_token_usages (
|
||||
id,
|
||||
interception_id,
|
||||
provider_response_id,
|
||||
input_tokens,
|
||||
output_tokens,
|
||||
metadata,
|
||||
created_at
|
||||
)
|
||||
VALUES (
|
||||
'5650db6c-0b7c-49e3-bb26-9b2ba0107e11',
|
||||
'c6d29c6e-26a3-4137-bb2e-9dfeef3c1c26',
|
||||
'chatcmpl-CG2s28QlpKIoooUtXuLTmGbdtyS1k',
|
||||
10950,
|
||||
118,
|
||||
'{}',
|
||||
'2025-09-15 12:45:21.674413+00'
|
||||
);
|
||||
INSERT INTO
|
||||
aibridge_user_prompts (
|
||||
id,
|
||||
interception_id,
|
||||
provider_response_id,
|
||||
prompt,
|
||||
metadata,
|
||||
created_at
|
||||
)
|
||||
VALUES (
|
||||
'1e76cb5b-7c34-4160-b604-a4256f856169',
|
||||
'c6d29c6e-26a3-4137-bb2e-9dfeef3c1c26',
|
||||
'chatcmpl-CG2s28QlpKIoooUtXuLTmGbdtyS1k',
|
||||
'how many workspaces do i have',
|
||||
'{}',
|
||||
'2025-09-15 12:45:21.674335+00'
|
||||
);
|
||||
INSERT INTO
|
||||
aibridge_tool_usages (
|
||||
id,
|
||||
interception_id,
|
||||
provider_response_id,
|
||||
tool,
|
||||
server_url,
|
||||
input,
|
||||
injected,
|
||||
invocation_error,
|
||||
metadata,
|
||||
created_at
|
||||
)
|
||||
VALUES (
|
||||
'351b440f-d605-4f37-8ceb-011f0377b695',
|
||||
'c6d29c6e-26a3-4137-bb2e-9dfeef3c1c26',
|
||||
'chatcmpl-CG2s28QlpKIoooUtXuLTmGbdtyS1k',
|
||||
'coder_list_workspaces',
|
||||
'http://localhost:3000/api/experimental/mcp/http',
|
||||
'{}',
|
||||
true,
|
||||
NULL,
|
||||
'{}',
|
||||
'2025-09-15 12:45:21.674413+00'
|
||||
);
|
||||
|
||||
@@ -763,11 +763,11 @@ func (q *sqlQuerier) CountAuthorizedConnectionLogs(ctx context.Context, arg Coun
|
||||
}
|
||||
|
||||
type aibridgeQuerier interface {
|
||||
ListAuthorizedAIBridgeInterceptions(ctx context.Context, arg ListAIBridgeInterceptionsParams, prepared rbac.PreparedAuthorized) ([]AIBridgeInterception, error)
|
||||
ListAuthorizedAIBridgeInterceptions(ctx context.Context, arg ListAIBridgeInterceptionsParams, prepared rbac.PreparedAuthorized) ([]ListAIBridgeInterceptionsRow, error)
|
||||
CountAuthorizedAIBridgeInterceptions(ctx context.Context, arg CountAIBridgeInterceptionsParams, prepared rbac.PreparedAuthorized) (int64, error)
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) ListAuthorizedAIBridgeInterceptions(ctx context.Context, arg ListAIBridgeInterceptionsParams, prepared rbac.PreparedAuthorized) ([]AIBridgeInterception, error) {
|
||||
func (q *sqlQuerier) ListAuthorizedAIBridgeInterceptions(ctx context.Context, arg ListAIBridgeInterceptionsParams, prepared rbac.PreparedAuthorized) ([]ListAIBridgeInterceptionsRow, error) {
|
||||
authorizedFilter, err := prepared.CompileToSQL(ctx, regosql.ConvertConfig{
|
||||
VariableConverter: regosql.AIBridgeInterceptionConverter(),
|
||||
})
|
||||
@@ -794,16 +794,20 @@ func (q *sqlQuerier) ListAuthorizedAIBridgeInterceptions(ctx context.Context, ar
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []AIBridgeInterception
|
||||
var items []ListAIBridgeInterceptionsRow
|
||||
for rows.Next() {
|
||||
var i AIBridgeInterception
|
||||
var i ListAIBridgeInterceptionsRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.InitiatorID,
|
||||
&i.Provider,
|
||||
&i.Model,
|
||||
&i.StartedAt,
|
||||
&i.Metadata,
|
||||
&i.AIBridgeInterception.ID,
|
||||
&i.AIBridgeInterception.InitiatorID,
|
||||
&i.AIBridgeInterception.Provider,
|
||||
&i.AIBridgeInterception.Model,
|
||||
&i.AIBridgeInterception.StartedAt,
|
||||
&i.AIBridgeInterception.Metadata,
|
||||
&i.VisibleUser.ID,
|
||||
&i.VisibleUser.Username,
|
||||
&i.VisibleUser.Name,
|
||||
&i.VisibleUser.AvatarURL,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -592,7 +592,7 @@ type sqlcQuerier interface {
|
||||
InsertWorkspaceProxy(ctx context.Context, arg InsertWorkspaceProxyParams) (WorkspaceProxy, error)
|
||||
InsertWorkspaceResource(ctx context.Context, arg InsertWorkspaceResourceParams) (WorkspaceResource, error)
|
||||
InsertWorkspaceResourceMetadata(ctx context.Context, arg InsertWorkspaceResourceMetadataParams) ([]WorkspaceResourceMetadatum, error)
|
||||
ListAIBridgeInterceptions(ctx context.Context, arg ListAIBridgeInterceptionsParams) ([]AIBridgeInterception, error)
|
||||
ListAIBridgeInterceptions(ctx context.Context, arg ListAIBridgeInterceptionsParams) ([]ListAIBridgeInterceptionsRow, error)
|
||||
ListAIBridgeTokenUsagesByInterceptionIDs(ctx context.Context, interceptionIds []uuid.UUID) ([]AIBridgeTokenUsage, error)
|
||||
ListAIBridgeToolUsagesByInterceptionIDs(ctx context.Context, interceptionIds []uuid.UUID) ([]AIBridgeToolUsage, error)
|
||||
ListAIBridgeUserPromptsByInterceptionIDs(ctx context.Context, interceptionIds []uuid.UUID) ([]AIBridgeUserPrompt, error)
|
||||
|
||||
@@ -528,9 +528,12 @@ func (q *sqlQuerier) InsertAIBridgeUserPrompt(ctx context.Context, arg InsertAIB
|
||||
|
||||
const listAIBridgeInterceptions = `-- name: ListAIBridgeInterceptions :many
|
||||
SELECT
|
||||
id, initiator_id, provider, model, started_at, metadata
|
||||
aibridge_interceptions.id, aibridge_interceptions.initiator_id, aibridge_interceptions.provider, aibridge_interceptions.model, aibridge_interceptions.started_at, aibridge_interceptions.metadata,
|
||||
visible_users.id, visible_users.username, visible_users.name, visible_users.avatar_url
|
||||
FROM
|
||||
aibridge_interceptions
|
||||
JOIN
|
||||
visible_users ON visible_users.id = aibridge_interceptions.initiator_id
|
||||
WHERE
|
||||
-- Filter by time frame
|
||||
CASE
|
||||
@@ -592,7 +595,12 @@ type ListAIBridgeInterceptionsParams struct {
|
||||
Limit int32 `db:"limit_" json:"limit_"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) ListAIBridgeInterceptions(ctx context.Context, arg ListAIBridgeInterceptionsParams) ([]AIBridgeInterception, error) {
|
||||
type ListAIBridgeInterceptionsRow struct {
|
||||
AIBridgeInterception AIBridgeInterception `db:"aibridge_interception" json:"aibridge_interception"`
|
||||
VisibleUser VisibleUser `db:"visible_user" json:"visible_user"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) ListAIBridgeInterceptions(ctx context.Context, arg ListAIBridgeInterceptionsParams) ([]ListAIBridgeInterceptionsRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, listAIBridgeInterceptions,
|
||||
arg.StartedAfter,
|
||||
arg.StartedBefore,
|
||||
@@ -607,16 +615,20 @@ func (q *sqlQuerier) ListAIBridgeInterceptions(ctx context.Context, arg ListAIBr
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []AIBridgeInterception
|
||||
var items []ListAIBridgeInterceptionsRow
|
||||
for rows.Next() {
|
||||
var i AIBridgeInterception
|
||||
var i ListAIBridgeInterceptionsRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.InitiatorID,
|
||||
&i.Provider,
|
||||
&i.Model,
|
||||
&i.StartedAt,
|
||||
&i.Metadata,
|
||||
&i.AIBridgeInterception.ID,
|
||||
&i.AIBridgeInterception.InitiatorID,
|
||||
&i.AIBridgeInterception.Provider,
|
||||
&i.AIBridgeInterception.Model,
|
||||
&i.AIBridgeInterception.StartedAt,
|
||||
&i.AIBridgeInterception.Metadata,
|
||||
&i.VisibleUser.ID,
|
||||
&i.VisibleUser.Username,
|
||||
&i.VisibleUser.Name,
|
||||
&i.VisibleUser.AvatarURL,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -111,9 +111,12 @@ WHERE
|
||||
|
||||
-- name: ListAIBridgeInterceptions :many
|
||||
SELECT
|
||||
*
|
||||
sqlc.embed(aibridge_interceptions),
|
||||
sqlc.embed(visible_users)
|
||||
FROM
|
||||
aibridge_interceptions
|
||||
JOIN
|
||||
visible_users ON visible_users.id = aibridge_interceptions.initiator_id
|
||||
WHERE
|
||||
-- Filter by time frame
|
||||
CASE
|
||||
|
||||
Reference in New Issue
Block a user