Skip to content

Commit 3cfc9b5

Browse files
committed
Revert "Reapply "remove server side agent filters for now""
This reverts commit 6c2deaf.
1 parent 6c2deaf commit 3cfc9b5

File tree

9 files changed

+260
-13
lines changed

9 files changed

+260
-13
lines changed

cmd/server/docs/docs.go

+9-2
Original file line numberDiff line numberDiff line change
@@ -1141,7 +1141,7 @@ const docTemplate = `{
11411141
"required": true
11421142
},
11431143
{
1144-
"description": "the agent's data (only 'name' and 'no_schedule' are read)",
1144+
"description": "the agent's data (only 'name', 'no_schedule', and 'filters' are read)",
11451145
"name": "agent",
11461146
"in": "body",
11471147
"required": true,
@@ -2419,7 +2419,7 @@ const docTemplate = `{
24192419
"required": true
24202420
},
24212421
{
2422-
"description": "the agent's data (only 'name' and 'no_schedule' are read)",
2422+
"description": "the agent's data (only 'name', 'no_schedule', and 'filters' are read)",
24232423
"name": "agent",
24242424
"in": "body",
24252425
"required": true,
@@ -4733,6 +4733,13 @@ const docTemplate = `{
47334733
"created": {
47344734
"type": "integer"
47354735
},
4736+
"filters": {
4737+
"description": "Server side enforced agent filters",
4738+
"type": "object",
4739+
"additionalProperties": {
4740+
"type": "string"
4741+
}
4742+
},
47364743
"id": {
47374744
"type": "integer"
47384745
},

server/api/agent.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ func PatchAgent(c *gin.Context) {
140140

141141
// Update allowed fields
142142
agent.Name = in.Name
143+
agent.Filters = in.Filters
143144
agent.NoSchedule = in.NoSchedule
144145
if agent.NoSchedule {
145146
server.Config.Services.Queue.KickAgentWorkers(agent.ID)
@@ -181,6 +182,7 @@ func PostAgent(c *gin.Context) {
181182
RepoID: model.IDNotSet,
182183
NoSchedule: in.NoSchedule,
183184
Token: model.GenerateNewAgentToken(),
185+
Filters: in.Filters,
184186
}
185187
if err = store.FromContext(c).AgentCreate(agent); err != nil {
186188
c.String(http.StatusInternalServerError, err.Error())
@@ -246,7 +248,7 @@ func DeleteAgent(c *gin.Context) {
246248
// @Tags Agents
247249
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
248250
// @Param org_id path int true "the organization's id"
249-
// @Param agent body Agent true "the agent's data (only 'name' and 'no_schedule' are read)"
251+
// @Param agent body Agent true "the agent's data (only 'name', 'no_schedule', and 'filters' are read)"
250252
func PostOrgAgent(c *gin.Context) {
251253
_store := store.FromContext(c)
252254
user := session.User(c)
@@ -271,6 +273,7 @@ func PostOrgAgent(c *gin.Context) {
271273
RepoID: model.IDNotSet,
272274
NoSchedule: in.NoSchedule,
273275
Token: model.GenerateNewAgentToken(),
276+
Filters: in.Filters,
274277
}
275278

276279
if err = _store.AgentCreate(agent); err != nil {
@@ -355,6 +358,7 @@ func PatchOrgAgent(c *gin.Context) {
355358

356359
// Update allowed fields
357360
agent.Name = in.Name
361+
agent.Filters = in.Filters
358362
agent.NoSchedule = in.NoSchedule
359363
if agent.NoSchedule {
360364
server.Config.Services.Queue.KickAgentWorkers(agent.ID)
@@ -438,7 +442,7 @@ func DeleteOrgAgent(c *gin.Context) {
438442
// @Tags Agents
439443
// @Param Authorization header string true "Insert your personal access token" default(Bearer <personal access token>)
440444
// @Param repo_id path int true "the repository's id"
441-
// @Param agent body Agent true "the agent's data (only 'name' and 'no_schedule' are read)"
445+
// @Param agent body Agent true "the agent's data (only 'name', 'no_schedule', and 'filters' are read)"
442446
func PostRepoAgent(c *gin.Context) {
443447
_store := store.FromContext(c)
444448
user := session.User(c)
@@ -469,6 +473,7 @@ func PostRepoAgent(c *gin.Context) {
469473
OrgID: repo.OrgID,
470474
RepoID: repoID,
471475
Token: model.GenerateNewAgentToken(),
476+
Filters: in.Filters,
472477
}
473478

474479
if err = _store.AgentCreate(agent); err != nil {
@@ -553,6 +558,7 @@ func PatchRepoAgent(c *gin.Context) {
553558

554559
// Update allowed fields
555560
agent.Name = in.Name
561+
agent.Filters = in.Filters
556562
agent.NoSchedule = in.NoSchedule
557563
if agent.NoSchedule {
558564
server.Config.Services.Queue.KickAgentWorkers(agent.ID)

server/model/agent.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ type Agent struct {
3939
OrgID int64 `json:"org_id" xorm:"INDEX 'org_id'"`
4040
// RepoID is counted as unset if set to -1, this is done to ensure a new(Agent) still enforce the OrgID check by default
4141
RepoID int64 `json:"repo_id" xorm:"INDEX 'repo_id'"`
42+
// Server side enforced agent filters
43+
Filters map[string]string `json:"filters" xorm:"'filters' json"`
4244
} // @name Agent
4345

4446
const (
@@ -61,7 +63,10 @@ func GenerateNewAgentToken() string {
6163
}
6264

6365
func (a *Agent) GetServerFilters() (map[string]string, error) {
64-
filters := make(map[string]string)
66+
filters := a.Filters
67+
if filters == nil {
68+
filters = make(map[string]string)
69+
}
6570

6671
// enforce filters for user and organization agents
6772
if a.OrgID != IDNotSet {

server/store/datastore/migration/015_add_org_and_repo_agents.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ import (
2424
)
2525

2626
type agentV015 struct {
27-
ID int64 `xorm:"pk autoincr 'id'"`
28-
OwnerID int64 `xorm:"INDEX 'owner_id'"`
29-
OrgID int64 `xorm:"INDEX 'org_id'"`
30-
RepoID int64 `xorm:"INDEX 'repo_id'"`
27+
ID int64 `xorm:"pk autoincr 'id'"`
28+
OwnerID int64 `xorm:"INDEX 'owner_id'"`
29+
OrgID int64 `xorm:"INDEX 'org_id'"`
30+
RepoID int64 `xorm:"INDEX 'repo_id'"`
31+
Filters map[string]string `xorm:"'filters' json"`
3132
}
3233

3334
func (agentV015) TableName() string {

web/src/assets/locales/en.json

+9-1
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,15 @@
315315
"never": "Never",
316316
"delete_confirm": "Do you really want to delete this agent? It will not be able to connect to the server anymore.",
317317
"edit_agent": "Edit agent",
318-
"delete_agent": "Delete agent"
318+
"delete_agent": "Delete agent",
319+
"filters": {
320+
"name": "Filters",
321+
"desc": "Server side enforced labels of agents to filter workflows",
322+
"key": "Label name ...",
323+
"value": "Filter value ...",
324+
"add": "Add filter",
325+
"delete": "Delete filter"
326+
}
319327
},
320328
"queue": {
321329
"queue": "Queue",

web/src/components/admin/settings/AdminAgentsTab.vue

+74-1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,58 @@
6161
/>
6262
</InputField>
6363

64+
<InputField v-slot="{ id }" :label="$t('admin.settings.agents.filters.name')">
65+
<span class="text-sm text-wp-text-alt-100 mb-2">{{ $t('admin.settings.agents.filters.desc') }}</span>
66+
<div class="flex flex-col gap-2">
67+
<div v-for="(filter, index) in filters" :key="index" class="flex gap-4">
68+
<TextField
69+
:id="`${id}-key-${index}`"
70+
v-model="filter.key"
71+
:placeholder="$t('admin.settings.agents.filters.key')"
72+
/>
73+
<TextField
74+
:id="`${id}-value-${index}`"
75+
v-model="filter.value"
76+
:placeholder="$t('admin.settings.agents.filters.value')"
77+
/>
78+
<div class="w-10 flex-shrink-0">
79+
<Button
80+
type="button"
81+
color="red"
82+
class="ml-auto"
83+
:title="$t('admin.settings.agents.filters.delete')"
84+
@click="deleteFilter(index)"
85+
>
86+
<Icon name="remove" />
87+
</Button>
88+
</div>
89+
</div>
90+
<div class="flex gap-4">
91+
<TextField
92+
:id="`${id}-new-key`"
93+
v-model="newFilterKey"
94+
:placeholder="$t('admin.settings.agents.filters.key')"
95+
/>
96+
<TextField
97+
:id="`${id}-new-value`"
98+
v-model="newFilterValue"
99+
:placeholder="$t('admin.settings.agents.filters.value')"
100+
/>
101+
<div class="w-10 flex-shrink-0">
102+
<Button
103+
type="button"
104+
color="green"
105+
class="ml-auto"
106+
:title="$t('admin.settings.agents.filters.add')"
107+
@click="addFilter"
108+
>
109+
<Icon name="plus" />
110+
</Button>
111+
</div>
112+
</div>
113+
</div>
114+
</InputField>
115+
64116
<InputField :label="$t('admin.settings.agents.no_schedule.name')">
65117
<Checkbox
66118
:model-value="selectedAgent.no_schedule || false"
@@ -142,6 +194,7 @@ import { useI18n } from 'vue-i18n';
142194
143195
import Badge from '~/components/atomic/Badge.vue';
144196
import Button from '~/components/atomic/Button.vue';
197+
import Icon from '~/components/atomic/Icon.vue';
145198
import IconButton from '~/components/atomic/IconButton.vue';
146199
import ListItem from '~/components/atomic/ListItem.vue';
147200
import Checkbox from '~/components/form/Checkbox.vue';
@@ -162,6 +215,9 @@ const { t } = useI18n();
162215
163216
const selectedAgent = ref<Partial<Agent>>();
164217
const isEditingAgent = computed(() => !!selectedAgent.value?.id);
218+
const filters = ref<Array<{ key: string; value: string }>>([]);
219+
const newFilterKey = ref('');
220+
const newFilterValue = ref('');
165221
166222
async function loadAgents(page: number): Promise<Agent[] | null> {
167223
return apiClient.getAgents({ page });
@@ -174,9 +230,12 @@ const { doSubmit: saveAgent, isLoading: isSaving } = useAsyncAction(async () =>
174230
throw new Error("Unexpected: Can't get agent");
175231
}
176232
233+
selectedAgent.value.filters = Object.fromEntries(filters.value.map((filter) => [filter.key, filter.value]));
234+
177235
if (isEditingAgent.value) {
178236
await apiClient.updateAgent(selectedAgent.value);
179237
selectedAgent.value = undefined;
238+
filters.value = [];
180239
} else {
181240
selectedAgent.value = await apiClient.createAgent(selectedAgent.value);
182241
}
@@ -200,9 +259,23 @@ const { doSubmit: deleteAgent, isLoading: isDeleting } = useAsyncAction(async (_
200259
201260
function editAgent(agent: Agent) {
202261
selectedAgent.value = cloneDeep(agent);
262+
filters.value = Object.entries(agent.filters || {}).map(([key, value]) => ({ key, value }));
203263
}
204264
205265
function showAddAgent() {
206-
selectedAgent.value = cloneDeep({ name: '' });
266+
selectedAgent.value = cloneDeep({ name: '', filters: {} });
267+
filters.value = [];
268+
}
269+
270+
function deleteFilter(index: number) {
271+
filters.value.splice(index, 1);
272+
}
273+
274+
function addFilter() {
275+
if (newFilterKey.value && newFilterValue.value) {
276+
filters.value.push({ key: newFilterKey.value, value: newFilterValue.value });
277+
newFilterKey.value = '';
278+
newFilterValue.value = '';
279+
}
207280
}
208281
</script>

web/src/components/org/settings/OrgAgentsTab.vue

+74-1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,58 @@
5555
/>
5656
</InputField>
5757

58+
<InputField v-slot="{ id }" :label="$t('admin.settings.agents.filters.name')">
59+
<span class="text-sm text-wp-text-alt-100 mb-2">{{ $t('admin.settings.agents.filters.desc') }}</span>
60+
<div class="flex flex-col gap-2">
61+
<div v-for="(filter, index) in filters" :key="index" class="flex gap-4">
62+
<TextField
63+
:id="`${id}-key-${index}`"
64+
v-model="filter.key"
65+
:placeholder="$t('admin.settings.agents.filters.key')"
66+
/>
67+
<TextField
68+
:id="`${id}-value-${index}`"
69+
v-model="filter.value"
70+
:placeholder="$t('admin.settings.agents.filters.value')"
71+
/>
72+
<div class="w-10 flex-shrink-0">
73+
<Button
74+
type="button"
75+
color="red"
76+
class="ml-auto"
77+
:title="$t('admin.settings.agents.filters.delete')"
78+
@click="deleteFilter(index)"
79+
>
80+
<Icon name="remove" />
81+
</Button>
82+
</div>
83+
</div>
84+
<div class="flex gap-4">
85+
<TextField
86+
:id="`${id}-new-key`"
87+
v-model="newFilterKey"
88+
:placeholder="$t('admin.settings.agents.filters.key')"
89+
/>
90+
<TextField
91+
:id="`${id}-new-value`"
92+
v-model="newFilterValue"
93+
:placeholder="$t('admin.settings.agents.filters.value')"
94+
/>
95+
<div class="w-10 flex-shrink-0">
96+
<Button
97+
type="button"
98+
color="green"
99+
class="ml-auto"
100+
:title="$t('admin.settings.agents.filters.add')"
101+
@click="addFilter"
102+
>
103+
<Icon name="plus" />
104+
</Button>
105+
</div>
106+
</div>
107+
</div>
108+
</InputField>
109+
58110
<InputField :label="$t('admin.settings.agents.no_schedule.name')">
59111
<Checkbox
60112
:model-value="selectedAgent.no_schedule || false"
@@ -136,6 +188,7 @@ import { useI18n } from 'vue-i18n';
136188
137189
import Badge from '~/components/atomic/Badge.vue';
138190
import Button from '~/components/atomic/Button.vue';
191+
import Icon from '~/components/atomic/Icon.vue';
139192
import IconButton from '~/components/atomic/IconButton.vue';
140193
import ListItem from '~/components/atomic/ListItem.vue';
141194
import Checkbox from '~/components/form/Checkbox.vue';
@@ -161,6 +214,9 @@ if (org === undefined) {
161214
162215
const selectedAgent = ref<Partial<Agent>>();
163216
const isEditingAgent = computed(() => !!selectedAgent.value?.id);
217+
const filters = ref<Array<{ key: string; value: string }>>([]);
218+
const newFilterKey = ref('');
219+
const newFilterValue = ref('');
164220
165221
async function loadAgents(page: number): Promise<Agent[] | null> {
166222
return apiClient.getOrgAgents(org!.value.id, { page });
@@ -173,9 +229,12 @@ const { doSubmit: saveAgent, isLoading: isSaving } = useAsyncAction(async () =>
173229
throw new Error("Unexpected: Can't get agent");
174230
}
175231
232+
selectedAgent.value.filters = Object.fromEntries(filters.value.map((filter) => [filter.key, filter.value]));
233+
176234
if (isEditingAgent.value) {
177235
await apiClient.updateOrgAgent(org.value.id, selectedAgent.value.id!, selectedAgent.value);
178236
selectedAgent.value = undefined;
237+
filters.value = [];
179238
} else {
180239
selectedAgent.value = await apiClient.createOrgAgent(org.value.id, selectedAgent.value);
181240
}
@@ -199,9 +258,23 @@ const { doSubmit: deleteAgent, isLoading: isDeleting } = useAsyncAction(async (_
199258
200259
function editAgent(agent: Agent) {
201260
selectedAgent.value = cloneDeep(agent);
261+
filters.value = Object.entries(agent.filters || {}).map(([key, value]) => ({ key, value }));
202262
}
203263
204264
function showAddAgent() {
205-
selectedAgent.value = cloneDeep({ name: '' });
265+
selectedAgent.value = cloneDeep({ name: '', filters: {} });
266+
filters.value = [];
267+
}
268+
269+
function deleteFilter(index: number) {
270+
filters.value.splice(index, 1);
271+
}
272+
273+
function addFilter() {
274+
if (newFilterKey.value && newFilterValue.value) {
275+
filters.value.push({ key: newFilterKey.value, value: newFilterValue.value });
276+
newFilterKey.value = '';
277+
newFilterValue.value = '';
278+
}
206279
}
207280
</script>

0 commit comments

Comments
 (0)