fix: emoji names regression, and webhook execution helper info

This commit is contained in:
jamesread 2026-06-01 00:29:48 +01:00
parent 6ca25bbdb4
commit 9ac6acefd0
11 changed files with 231 additions and 108 deletions

View File

@ -10,7 +10,7 @@
"stylelint-config-standard": "^40.0.0"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"test": "node --test resources/vue/components/*.test.mjs"
},
"author": "",
"parcelIgnore": [

View File

@ -111,6 +111,16 @@ export declare type ActionWebhookExecHint = Message<"olivetin.api.v1.ActionWebho
* @generated from field: string match_path = 2;
*/
matchPath: string;
/**
* @generated from field: map<string, string> match_headers = 3;
*/
matchHeaders: { [key: string]: string };
/**
* @generated from field: map<string, string> match_query = 4;
*/
matchQuery: { [key: string]: string };
};
/**

File diff suppressed because one or more lines are too long

View File

@ -7,6 +7,7 @@
height="1em"
class="action-icon-glyph-svg"
/>
<span v-else-if="decodedTextGlyphIsHtml" v-html="decodedTextGlyph"></span>
<span v-else v-text="decodedTextGlyph"></span>
</span>
</template>
@ -15,6 +16,7 @@
import { computed } from 'vue'
import { HugeiconsIcon } from '@hugeicons/vue'
import { CommandLineIcon } from '@hugeicons/core-free-icons'
import { decodeHtmlEntities, glyphLooksLikeHtml } from './actionIconGlyphHelpers.mjs'
const hugeiconsPrefix = 'hugeicons:'
@ -46,16 +48,6 @@ const hugeiconsModel = computed(() => {
return iconModel ?? CommandLineIcon
})
function decodeHtmlEntities(text) {
return text.replace(/&#x([0-9a-fA-F]+);?/g, (_, hex) => {
const codePoint = Number.parseInt(hex, 16)
return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : ''
}).replace(/&#(\d+);?/g, (_, decimal) => {
const codePoint = Number.parseInt(decimal, 10)
return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : ''
})
}
const decodedTextGlyph = computed(() => {
if (hugeiconsModel.value) {
return ''
@ -63,6 +55,8 @@ const decodedTextGlyph = computed(() => {
return decodeHtmlEntities(props.glyph)
})
const decodedTextGlyphIsHtml = computed(() => glyphLooksLikeHtml(decodedTextGlyph.value))
</script>
<style scoped>

View File

@ -0,0 +1,38 @@
const fallbackNamedHtmlEntities = {
amp: '&',
apos: "'",
darr: '\u2193',
gt: '>',
laquo: '\u00ab',
larr: '\u2190',
nbsp: '\u00a0',
quot: '"',
raquo: '\u00bb',
rarr: '\u2192',
uarr: '\u2191',
}
export function decodeHtmlEntities(text) {
if (typeof document !== 'undefined') {
const textarea = document.createElement('textarea')
textarea.innerHTML = text
return textarea.value
}
return text.replace(/&#x([0-9a-fA-F]+);?/g, (_, hex) => {
const codePoint = Number.parseInt(hex, 16)
return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : ''
}).replace(/&#(\d+);?/g, (_, decimal) => {
const codePoint = Number.parseInt(decimal, 10)
return Number.isFinite(codePoint) ? String.fromCodePoint(codePoint) : ''
}).replace(/&([a-zA-Z][a-zA-Z0-9]+);?/g, (entity, name) => {
return fallbackNamedHtmlEntities[name] ?? entity
})
}
export function glyphLooksLikeHtml(text) {
const trimmedText = text.trim()
return trimmedText.startsWith('<') || /<img\b/i.test(text) || /\/custom-webui\//i.test(text)
}

View File

@ -0,0 +1,20 @@
import test from 'node:test'
import assert from 'node:assert/strict'
import { decodeHtmlEntities, glyphLooksLikeHtml } from './actionIconGlyphHelpers.mjs'
test('decodeHtmlEntities decodes named entity icons as plain glyph text', () => {
assert.equal(decodeHtmlEntities('&laquo;'), '\u00ab')
assert.equal(decodeHtmlEntities('&rarr;'), '\u2192')
assert.equal(decodeHtmlEntities('&laquo; next &rarr;'), '\u00ab next \u2192')
})
test('decoded named entity icons are not treated as HTML markup', () => {
const decodedGlyph = decodeHtmlEntities('&rarr;')
assert.equal(glyphLooksLikeHtml(decodedGlyph), false)
})
test('decodeHtmlEntities keeps existing numeric entity icon support', () => {
assert.equal(decodeHtmlEntities('&#x1f4a9;'), '\ud83d\udca9')
assert.equal(decodeHtmlEntities('&#128190;'), '\ud83d\udcbe')
})

View File

@ -76,7 +76,9 @@
<li v-for="(wh, idx) in action.execOnWebhooks" :key="'wh-' + idx">
<span v-if="wh.template">template: <code>{{ wh.template }}</code></span>
<span v-if="wh.matchPath"> · matchPath: <code>{{ wh.matchPath }}</code></span>
<span v-if="!wh.template && !wh.matchPath">Webhook trigger (no template or match path in response)</span>
<span v-if="nonEmptyObject(wh.matchHeaders)"> · matchHeaders: <code>{{ wh.matchHeaders }}</code></span>
<span v-if="nonEmptyObject(wh.matchQuery)"> · matchQuery: <code>{{ wh.matchQuery }}</code></span>
<span v-if="!webhookHasCondition(wh)">Webhook trigger (no conditions in response)</span>
</li>
</ul>
</template>
@ -117,6 +119,14 @@ function nonEmptyList(list) {
return Array.isArray(list) && list.length > 0
}
function nonEmptyObject(object) {
return object && Object.keys(object).length > 0
}
function webhookHasCondition(webhook) {
return webhook.template || webhook.matchPath || nonEmptyObject(webhook.matchHeaders) || nonEmptyObject(webhook.matchQuery)
}
const hasConfiguredTriggers = computed(() => {
const a = action.value
if (!a) {

View File

@ -25,6 +25,8 @@ message Action {
message ActionWebhookExecHint {
string template = 1;
string match_path = 2;
map<string, string> match_headers = 3;
map<string, string> match_query = 4;
}
message ActionArgument {

View File

@ -181,6 +181,8 @@ type ActionWebhookExecHint struct {
state protoimpl.MessageState `protogen:"open.v1"`
Template string `protobuf:"bytes,1,opt,name=template,proto3" json:"template,omitempty"`
MatchPath string `protobuf:"bytes,2,opt,name=match_path,json=matchPath,proto3" json:"match_path,omitempty"`
MatchHeaders map[string]string `protobuf:"bytes,3,rep,name=match_headers,json=matchHeaders,proto3" json:"match_headers,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
MatchQuery map[string]string `protobuf:"bytes,4,rep,name=match_query,json=matchQuery,proto3" json:"match_query,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@ -229,6 +231,20 @@ func (x *ActionWebhookExecHint) GetMatchPath() string {
return ""
}
func (x *ActionWebhookExecHint) GetMatchHeaders() map[string]string {
if x != nil {
return x.MatchHeaders
}
return nil
}
func (x *ActionWebhookExecHint) GetMatchQuery() map[string]string {
if x != nil {
return x.MatchQuery
}
return nil
}
type ActionArgument struct {
state protoimpl.MessageState `protogen:"open.v1"`
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
@ -4022,11 +4038,20 @@ const file_olivetin_api_v1_olivetin_proto_rawDesc = "" +
"\x1bexec_on_file_created_in_dir\x18\f \x03(\tR\x16execOnFileCreatedInDir\x12;\n" +
"\x1bexec_on_file_changed_in_dir\x18\r \x03(\tR\x16execOnFileChangedInDir\x121\n" +
"\x15exec_on_calendar_file\x18\x0e \x01(\tR\x12execOnCalendarFile\x12P\n" +
"\x10exec_on_webhooks\x18\x0f \x03(\v2&.olivetin.api.v1.ActionWebhookExecHintR\x0eexecOnWebhooks\"R\n" +
"\x10exec_on_webhooks\x18\x0f \x03(\v2&.olivetin.api.v1.ActionWebhookExecHintR\x0eexecOnWebhooks\"\x8a\x03\n" +
"\x15ActionWebhookExecHint\x12\x1a\n" +
"\btemplate\x18\x01 \x01(\tR\btemplate\x12\x1d\n" +
"\n" +
"match_path\x18\x02 \x01(\tR\tmatchPath\"\xa2\x03\n" +
"match_path\x18\x02 \x01(\tR\tmatchPath\x12]\n" +
"\rmatch_headers\x18\x03 \x03(\v28.olivetin.api.v1.ActionWebhookExecHint.MatchHeadersEntryR\fmatchHeaders\x12W\n" +
"\vmatch_query\x18\x04 \x03(\v26.olivetin.api.v1.ActionWebhookExecHint.MatchQueryEntryR\n" +
"matchQuery\x1a?\n" +
"\x11MatchHeadersEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\x1a=\n" +
"\x0fMatchQueryEntry\x12\x10\n" +
"\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" +
"\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xa2\x03\n" +
"\x0eActionArgument\x12\x12\n" +
"\x04name\x18\x01 \x01(\tR\x04name\x12\x14\n" +
"\x05title\x18\x02 \x01(\tR\x05title\x12\x12\n" +
@ -4330,7 +4355,7 @@ func file_olivetin_api_v1_olivetin_proto_rawDescGZIP() []byte {
return file_olivetin_api_v1_olivetin_proto_rawDescData
}
var file_olivetin_api_v1_olivetin_proto_msgTypes = make([]protoimpl.MessageInfo, 73)
var file_olivetin_api_v1_olivetin_proto_msgTypes = make([]protoimpl.MessageInfo, 75)
var file_olivetin_api_v1_olivetin_proto_goTypes = []any{
(*Action)(nil), // 0: olivetin.api.v1.Action
(*ActionWebhookExecHint)(nil), // 1: olivetin.api.v1.ActionWebhookExecHint
@ -4401,99 +4426,103 @@ var file_olivetin_api_v1_olivetin_proto_goTypes = []any{
(*EntityDefinition)(nil), // 66: olivetin.api.v1.EntityDefinition
(*GetEntityRequest)(nil), // 67: olivetin.api.v1.GetEntityRequest
(*RestartActionRequest)(nil), // 68: olivetin.api.v1.RestartActionRequest
nil, // 69: olivetin.api.v1.ActionArgument.SuggestionsEntry
nil, // 70: olivetin.api.v1.Entity.FieldsEntry
nil, // 71: olivetin.api.v1.DumpVarsResponse.ContentsEntry
nil, // 72: olivetin.api.v1.DumpPublicIdActionMapResponse.ContentsEntry
nil, // 69: olivetin.api.v1.ActionWebhookExecHint.MatchHeadersEntry
nil, // 70: olivetin.api.v1.ActionWebhookExecHint.MatchQueryEntry
nil, // 71: olivetin.api.v1.ActionArgument.SuggestionsEntry
nil, // 72: olivetin.api.v1.Entity.FieldsEntry
nil, // 73: olivetin.api.v1.DumpVarsResponse.ContentsEntry
nil, // 74: olivetin.api.v1.DumpPublicIdActionMapResponse.ContentsEntry
}
var file_olivetin_api_v1_olivetin_proto_depIdxs = []int32{
2, // 0: olivetin.api.v1.Action.arguments:type_name -> olivetin.api.v1.ActionArgument
1, // 1: olivetin.api.v1.Action.exec_on_webhooks:type_name -> olivetin.api.v1.ActionWebhookExecHint
3, // 2: olivetin.api.v1.ActionArgument.choices:type_name -> olivetin.api.v1.ActionArgumentChoice
69, // 3: olivetin.api.v1.ActionArgument.suggestions:type_name -> olivetin.api.v1.ActionArgument.SuggestionsEntry
70, // 4: olivetin.api.v1.Entity.fields:type_name -> olivetin.api.v1.Entity.FieldsEntry
8, // 5: olivetin.api.v1.GetDashboardResponse.dashboard:type_name -> olivetin.api.v1.Dashboard
9, // 6: olivetin.api.v1.Dashboard.contents:type_name -> olivetin.api.v1.DashboardComponent
9, // 7: olivetin.api.v1.DashboardComponent.contents:type_name -> olivetin.api.v1.DashboardComponent
0, // 8: olivetin.api.v1.DashboardComponent.action:type_name -> olivetin.api.v1.Action
11, // 9: olivetin.api.v1.StartActionRequest.arguments:type_name -> olivetin.api.v1.StartActionArgument
11, // 10: olivetin.api.v1.StartActionAndWaitRequest.arguments:type_name -> olivetin.api.v1.StartActionArgument
20, // 11: olivetin.api.v1.StartActionAndWaitResponse.log_entry:type_name -> olivetin.api.v1.LogEntry
20, // 12: olivetin.api.v1.StartActionByGetAndWaitResponse.log_entry:type_name -> olivetin.api.v1.LogEntry
20, // 13: olivetin.api.v1.GetLogsResponse.logs:type_name -> olivetin.api.v1.LogEntry
20, // 14: olivetin.api.v1.GetActionLogsResponse.logs:type_name -> olivetin.api.v1.LogEntry
20, // 15: olivetin.api.v1.ExecutionStatusResponse.log_entry:type_name -> olivetin.api.v1.LogEntry
71, // 16: olivetin.api.v1.DumpVarsResponse.contents:type_name -> olivetin.api.v1.DumpVarsResponse.ContentsEntry
72, // 17: olivetin.api.v1.DumpPublicIdActionMapResponse.contents:type_name -> olivetin.api.v1.DumpPublicIdActionMapResponse.ContentsEntry
44, // 18: olivetin.api.v1.EventStreamResponse.entity_changed:type_name -> olivetin.api.v1.EventEntityChanged
45, // 19: olivetin.api.v1.EventStreamResponse.config_changed:type_name -> olivetin.api.v1.EventConfigChanged
46, // 20: olivetin.api.v1.EventStreamResponse.execution_finished:type_name -> olivetin.api.v1.EventExecutionFinished
47, // 21: olivetin.api.v1.EventStreamResponse.execution_started:type_name -> olivetin.api.v1.EventExecutionStarted
43, // 22: olivetin.api.v1.EventStreamResponse.output_chunk:type_name -> olivetin.api.v1.EventOutputChunk
20, // 23: olivetin.api.v1.EventExecutionFinished.log_entry:type_name -> olivetin.api.v1.LogEntry
20, // 24: olivetin.api.v1.EventExecutionStarted.log_entry:type_name -> olivetin.api.v1.LogEntry
61, // 25: olivetin.api.v1.InitResponse.oAuth2Providers:type_name -> olivetin.api.v1.OAuth2Provider
60, // 26: olivetin.api.v1.InitResponse.additionalLinks:type_name -> olivetin.api.v1.AdditionalLink
6, // 27: olivetin.api.v1.InitResponse.effective_policy:type_name -> olivetin.api.v1.EffectivePolicy
0, // 28: olivetin.api.v1.GetActionBindingResponse.action:type_name -> olivetin.api.v1.Action
66, // 29: olivetin.api.v1.GetEntitiesResponse.entity_definitions:type_name -> olivetin.api.v1.EntityDefinition
4, // 30: olivetin.api.v1.EntityDefinition.instances:type_name -> olivetin.api.v1.Entity
36, // 31: olivetin.api.v1.DumpPublicIdActionMapResponse.ContentsEntry.value:type_name -> olivetin.api.v1.DebugBinding
7, // 32: olivetin.api.v1.OliveTinApiService.GetDashboard:input_type -> olivetin.api.v1.GetDashboardRequest
10, // 33: olivetin.api.v1.OliveTinApiService.StartAction:input_type -> olivetin.api.v1.StartActionRequest
13, // 34: olivetin.api.v1.OliveTinApiService.StartActionAndWait:input_type -> olivetin.api.v1.StartActionAndWaitRequest
15, // 35: olivetin.api.v1.OliveTinApiService.StartActionByGet:input_type -> olivetin.api.v1.StartActionByGetRequest
17, // 36: olivetin.api.v1.OliveTinApiService.StartActionByGetAndWait:input_type -> olivetin.api.v1.StartActionByGetAndWaitRequest
68, // 37: olivetin.api.v1.OliveTinApiService.RestartAction:input_type -> olivetin.api.v1.RestartActionRequest
48, // 38: olivetin.api.v1.OliveTinApiService.KillAction:input_type -> olivetin.api.v1.KillActionRequest
28, // 39: olivetin.api.v1.OliveTinApiService.ExecutionStatus:input_type -> olivetin.api.v1.ExecutionStatusRequest
19, // 40: olivetin.api.v1.OliveTinApiService.GetLogs:input_type -> olivetin.api.v1.GetLogsRequest
22, // 41: olivetin.api.v1.OliveTinApiService.GetActionLogs:input_type -> olivetin.api.v1.GetActionLogsRequest
24, // 42: olivetin.api.v1.OliveTinApiService.ValidateArgumentType:input_type -> olivetin.api.v1.ValidateArgumentTypeRequest
30, // 43: olivetin.api.v1.OliveTinApiService.WhoAmI:input_type -> olivetin.api.v1.WhoAmIRequest
32, // 44: olivetin.api.v1.OliveTinApiService.SosReport:input_type -> olivetin.api.v1.SosReportRequest
34, // 45: olivetin.api.v1.OliveTinApiService.DumpVars:input_type -> olivetin.api.v1.DumpVarsRequest
37, // 46: olivetin.api.v1.OliveTinApiService.DumpPublicIdActionMap:input_type -> olivetin.api.v1.DumpPublicIdActionMapRequest
39, // 47: olivetin.api.v1.OliveTinApiService.GetReadyz:input_type -> olivetin.api.v1.GetReadyzRequest
50, // 48: olivetin.api.v1.OliveTinApiService.LocalUserLogin:input_type -> olivetin.api.v1.LocalUserLoginRequest
52, // 49: olivetin.api.v1.OliveTinApiService.PasswordHash:input_type -> olivetin.api.v1.PasswordHashRequest
54, // 50: olivetin.api.v1.OliveTinApiService.Logout:input_type -> olivetin.api.v1.LogoutRequest
41, // 51: olivetin.api.v1.OliveTinApiService.EventStream:input_type -> olivetin.api.v1.EventStreamRequest
56, // 52: olivetin.api.v1.OliveTinApiService.GetDiagnostics:input_type -> olivetin.api.v1.GetDiagnosticsRequest
58, // 53: olivetin.api.v1.OliveTinApiService.Init:input_type -> olivetin.api.v1.InitRequest
62, // 54: olivetin.api.v1.OliveTinApiService.GetActionBinding:input_type -> olivetin.api.v1.GetActionBindingRequest
64, // 55: olivetin.api.v1.OliveTinApiService.GetEntities:input_type -> olivetin.api.v1.GetEntitiesRequest
67, // 56: olivetin.api.v1.OliveTinApiService.GetEntity:input_type -> olivetin.api.v1.GetEntityRequest
5, // 57: olivetin.api.v1.OliveTinApiService.GetDashboard:output_type -> olivetin.api.v1.GetDashboardResponse
12, // 58: olivetin.api.v1.OliveTinApiService.StartAction:output_type -> olivetin.api.v1.StartActionResponse
14, // 59: olivetin.api.v1.OliveTinApiService.StartActionAndWait:output_type -> olivetin.api.v1.StartActionAndWaitResponse
16, // 60: olivetin.api.v1.OliveTinApiService.StartActionByGet:output_type -> olivetin.api.v1.StartActionByGetResponse
18, // 61: olivetin.api.v1.OliveTinApiService.StartActionByGetAndWait:output_type -> olivetin.api.v1.StartActionByGetAndWaitResponse
12, // 62: olivetin.api.v1.OliveTinApiService.RestartAction:output_type -> olivetin.api.v1.StartActionResponse
49, // 63: olivetin.api.v1.OliveTinApiService.KillAction:output_type -> olivetin.api.v1.KillActionResponse
29, // 64: olivetin.api.v1.OliveTinApiService.ExecutionStatus:output_type -> olivetin.api.v1.ExecutionStatusResponse
21, // 65: olivetin.api.v1.OliveTinApiService.GetLogs:output_type -> olivetin.api.v1.GetLogsResponse
23, // 66: olivetin.api.v1.OliveTinApiService.GetActionLogs:output_type -> olivetin.api.v1.GetActionLogsResponse
25, // 67: olivetin.api.v1.OliveTinApiService.ValidateArgumentType:output_type -> olivetin.api.v1.ValidateArgumentTypeResponse
31, // 68: olivetin.api.v1.OliveTinApiService.WhoAmI:output_type -> olivetin.api.v1.WhoAmIResponse
33, // 69: olivetin.api.v1.OliveTinApiService.SosReport:output_type -> olivetin.api.v1.SosReportResponse
35, // 70: olivetin.api.v1.OliveTinApiService.DumpVars:output_type -> olivetin.api.v1.DumpVarsResponse
38, // 71: olivetin.api.v1.OliveTinApiService.DumpPublicIdActionMap:output_type -> olivetin.api.v1.DumpPublicIdActionMapResponse
40, // 72: olivetin.api.v1.OliveTinApiService.GetReadyz:output_type -> olivetin.api.v1.GetReadyzResponse
51, // 73: olivetin.api.v1.OliveTinApiService.LocalUserLogin:output_type -> olivetin.api.v1.LocalUserLoginResponse
53, // 74: olivetin.api.v1.OliveTinApiService.PasswordHash:output_type -> olivetin.api.v1.PasswordHashResponse
55, // 75: olivetin.api.v1.OliveTinApiService.Logout:output_type -> olivetin.api.v1.LogoutResponse
42, // 76: olivetin.api.v1.OliveTinApiService.EventStream:output_type -> olivetin.api.v1.EventStreamResponse
57, // 77: olivetin.api.v1.OliveTinApiService.GetDiagnostics:output_type -> olivetin.api.v1.GetDiagnosticsResponse
59, // 78: olivetin.api.v1.OliveTinApiService.Init:output_type -> olivetin.api.v1.InitResponse
63, // 79: olivetin.api.v1.OliveTinApiService.GetActionBinding:output_type -> olivetin.api.v1.GetActionBindingResponse
65, // 80: olivetin.api.v1.OliveTinApiService.GetEntities:output_type -> olivetin.api.v1.GetEntitiesResponse
4, // 81: olivetin.api.v1.OliveTinApiService.GetEntity:output_type -> olivetin.api.v1.Entity
57, // [57:82] is the sub-list for method output_type
32, // [32:57] is the sub-list for method input_type
32, // [32:32] is the sub-list for extension type_name
32, // [32:32] is the sub-list for extension extendee
0, // [0:32] is the sub-list for field type_name
69, // 2: olivetin.api.v1.ActionWebhookExecHint.match_headers:type_name -> olivetin.api.v1.ActionWebhookExecHint.MatchHeadersEntry
70, // 3: olivetin.api.v1.ActionWebhookExecHint.match_query:type_name -> olivetin.api.v1.ActionWebhookExecHint.MatchQueryEntry
3, // 4: olivetin.api.v1.ActionArgument.choices:type_name -> olivetin.api.v1.ActionArgumentChoice
71, // 5: olivetin.api.v1.ActionArgument.suggestions:type_name -> olivetin.api.v1.ActionArgument.SuggestionsEntry
72, // 6: olivetin.api.v1.Entity.fields:type_name -> olivetin.api.v1.Entity.FieldsEntry
8, // 7: olivetin.api.v1.GetDashboardResponse.dashboard:type_name -> olivetin.api.v1.Dashboard
9, // 8: olivetin.api.v1.Dashboard.contents:type_name -> olivetin.api.v1.DashboardComponent
9, // 9: olivetin.api.v1.DashboardComponent.contents:type_name -> olivetin.api.v1.DashboardComponent
0, // 10: olivetin.api.v1.DashboardComponent.action:type_name -> olivetin.api.v1.Action
11, // 11: olivetin.api.v1.StartActionRequest.arguments:type_name -> olivetin.api.v1.StartActionArgument
11, // 12: olivetin.api.v1.StartActionAndWaitRequest.arguments:type_name -> olivetin.api.v1.StartActionArgument
20, // 13: olivetin.api.v1.StartActionAndWaitResponse.log_entry:type_name -> olivetin.api.v1.LogEntry
20, // 14: olivetin.api.v1.StartActionByGetAndWaitResponse.log_entry:type_name -> olivetin.api.v1.LogEntry
20, // 15: olivetin.api.v1.GetLogsResponse.logs:type_name -> olivetin.api.v1.LogEntry
20, // 16: olivetin.api.v1.GetActionLogsResponse.logs:type_name -> olivetin.api.v1.LogEntry
20, // 17: olivetin.api.v1.ExecutionStatusResponse.log_entry:type_name -> olivetin.api.v1.LogEntry
73, // 18: olivetin.api.v1.DumpVarsResponse.contents:type_name -> olivetin.api.v1.DumpVarsResponse.ContentsEntry
74, // 19: olivetin.api.v1.DumpPublicIdActionMapResponse.contents:type_name -> olivetin.api.v1.DumpPublicIdActionMapResponse.ContentsEntry
44, // 20: olivetin.api.v1.EventStreamResponse.entity_changed:type_name -> olivetin.api.v1.EventEntityChanged
45, // 21: olivetin.api.v1.EventStreamResponse.config_changed:type_name -> olivetin.api.v1.EventConfigChanged
46, // 22: olivetin.api.v1.EventStreamResponse.execution_finished:type_name -> olivetin.api.v1.EventExecutionFinished
47, // 23: olivetin.api.v1.EventStreamResponse.execution_started:type_name -> olivetin.api.v1.EventExecutionStarted
43, // 24: olivetin.api.v1.EventStreamResponse.output_chunk:type_name -> olivetin.api.v1.EventOutputChunk
20, // 25: olivetin.api.v1.EventExecutionFinished.log_entry:type_name -> olivetin.api.v1.LogEntry
20, // 26: olivetin.api.v1.EventExecutionStarted.log_entry:type_name -> olivetin.api.v1.LogEntry
61, // 27: olivetin.api.v1.InitResponse.oAuth2Providers:type_name -> olivetin.api.v1.OAuth2Provider
60, // 28: olivetin.api.v1.InitResponse.additionalLinks:type_name -> olivetin.api.v1.AdditionalLink
6, // 29: olivetin.api.v1.InitResponse.effective_policy:type_name -> olivetin.api.v1.EffectivePolicy
0, // 30: olivetin.api.v1.GetActionBindingResponse.action:type_name -> olivetin.api.v1.Action
66, // 31: olivetin.api.v1.GetEntitiesResponse.entity_definitions:type_name -> olivetin.api.v1.EntityDefinition
4, // 32: olivetin.api.v1.EntityDefinition.instances:type_name -> olivetin.api.v1.Entity
36, // 33: olivetin.api.v1.DumpPublicIdActionMapResponse.ContentsEntry.value:type_name -> olivetin.api.v1.DebugBinding
7, // 34: olivetin.api.v1.OliveTinApiService.GetDashboard:input_type -> olivetin.api.v1.GetDashboardRequest
10, // 35: olivetin.api.v1.OliveTinApiService.StartAction:input_type -> olivetin.api.v1.StartActionRequest
13, // 36: olivetin.api.v1.OliveTinApiService.StartActionAndWait:input_type -> olivetin.api.v1.StartActionAndWaitRequest
15, // 37: olivetin.api.v1.OliveTinApiService.StartActionByGet:input_type -> olivetin.api.v1.StartActionByGetRequest
17, // 38: olivetin.api.v1.OliveTinApiService.StartActionByGetAndWait:input_type -> olivetin.api.v1.StartActionByGetAndWaitRequest
68, // 39: olivetin.api.v1.OliveTinApiService.RestartAction:input_type -> olivetin.api.v1.RestartActionRequest
48, // 40: olivetin.api.v1.OliveTinApiService.KillAction:input_type -> olivetin.api.v1.KillActionRequest
28, // 41: olivetin.api.v1.OliveTinApiService.ExecutionStatus:input_type -> olivetin.api.v1.ExecutionStatusRequest
19, // 42: olivetin.api.v1.OliveTinApiService.GetLogs:input_type -> olivetin.api.v1.GetLogsRequest
22, // 43: olivetin.api.v1.OliveTinApiService.GetActionLogs:input_type -> olivetin.api.v1.GetActionLogsRequest
24, // 44: olivetin.api.v1.OliveTinApiService.ValidateArgumentType:input_type -> olivetin.api.v1.ValidateArgumentTypeRequest
30, // 45: olivetin.api.v1.OliveTinApiService.WhoAmI:input_type -> olivetin.api.v1.WhoAmIRequest
32, // 46: olivetin.api.v1.OliveTinApiService.SosReport:input_type -> olivetin.api.v1.SosReportRequest
34, // 47: olivetin.api.v1.OliveTinApiService.DumpVars:input_type -> olivetin.api.v1.DumpVarsRequest
37, // 48: olivetin.api.v1.OliveTinApiService.DumpPublicIdActionMap:input_type -> olivetin.api.v1.DumpPublicIdActionMapRequest
39, // 49: olivetin.api.v1.OliveTinApiService.GetReadyz:input_type -> olivetin.api.v1.GetReadyzRequest
50, // 50: olivetin.api.v1.OliveTinApiService.LocalUserLogin:input_type -> olivetin.api.v1.LocalUserLoginRequest
52, // 51: olivetin.api.v1.OliveTinApiService.PasswordHash:input_type -> olivetin.api.v1.PasswordHashRequest
54, // 52: olivetin.api.v1.OliveTinApiService.Logout:input_type -> olivetin.api.v1.LogoutRequest
41, // 53: olivetin.api.v1.OliveTinApiService.EventStream:input_type -> olivetin.api.v1.EventStreamRequest
56, // 54: olivetin.api.v1.OliveTinApiService.GetDiagnostics:input_type -> olivetin.api.v1.GetDiagnosticsRequest
58, // 55: olivetin.api.v1.OliveTinApiService.Init:input_type -> olivetin.api.v1.InitRequest
62, // 56: olivetin.api.v1.OliveTinApiService.GetActionBinding:input_type -> olivetin.api.v1.GetActionBindingRequest
64, // 57: olivetin.api.v1.OliveTinApiService.GetEntities:input_type -> olivetin.api.v1.GetEntitiesRequest
67, // 58: olivetin.api.v1.OliveTinApiService.GetEntity:input_type -> olivetin.api.v1.GetEntityRequest
5, // 59: olivetin.api.v1.OliveTinApiService.GetDashboard:output_type -> olivetin.api.v1.GetDashboardResponse
12, // 60: olivetin.api.v1.OliveTinApiService.StartAction:output_type -> olivetin.api.v1.StartActionResponse
14, // 61: olivetin.api.v1.OliveTinApiService.StartActionAndWait:output_type -> olivetin.api.v1.StartActionAndWaitResponse
16, // 62: olivetin.api.v1.OliveTinApiService.StartActionByGet:output_type -> olivetin.api.v1.StartActionByGetResponse
18, // 63: olivetin.api.v1.OliveTinApiService.StartActionByGetAndWait:output_type -> olivetin.api.v1.StartActionByGetAndWaitResponse
12, // 64: olivetin.api.v1.OliveTinApiService.RestartAction:output_type -> olivetin.api.v1.StartActionResponse
49, // 65: olivetin.api.v1.OliveTinApiService.KillAction:output_type -> olivetin.api.v1.KillActionResponse
29, // 66: olivetin.api.v1.OliveTinApiService.ExecutionStatus:output_type -> olivetin.api.v1.ExecutionStatusResponse
21, // 67: olivetin.api.v1.OliveTinApiService.GetLogs:output_type -> olivetin.api.v1.GetLogsResponse
23, // 68: olivetin.api.v1.OliveTinApiService.GetActionLogs:output_type -> olivetin.api.v1.GetActionLogsResponse
25, // 69: olivetin.api.v1.OliveTinApiService.ValidateArgumentType:output_type -> olivetin.api.v1.ValidateArgumentTypeResponse
31, // 70: olivetin.api.v1.OliveTinApiService.WhoAmI:output_type -> olivetin.api.v1.WhoAmIResponse
33, // 71: olivetin.api.v1.OliveTinApiService.SosReport:output_type -> olivetin.api.v1.SosReportResponse
35, // 72: olivetin.api.v1.OliveTinApiService.DumpVars:output_type -> olivetin.api.v1.DumpVarsResponse
38, // 73: olivetin.api.v1.OliveTinApiService.DumpPublicIdActionMap:output_type -> olivetin.api.v1.DumpPublicIdActionMapResponse
40, // 74: olivetin.api.v1.OliveTinApiService.GetReadyz:output_type -> olivetin.api.v1.GetReadyzResponse
51, // 75: olivetin.api.v1.OliveTinApiService.LocalUserLogin:output_type -> olivetin.api.v1.LocalUserLoginResponse
53, // 76: olivetin.api.v1.OliveTinApiService.PasswordHash:output_type -> olivetin.api.v1.PasswordHashResponse
55, // 77: olivetin.api.v1.OliveTinApiService.Logout:output_type -> olivetin.api.v1.LogoutResponse
42, // 78: olivetin.api.v1.OliveTinApiService.EventStream:output_type -> olivetin.api.v1.EventStreamResponse
57, // 79: olivetin.api.v1.OliveTinApiService.GetDiagnostics:output_type -> olivetin.api.v1.GetDiagnosticsResponse
59, // 80: olivetin.api.v1.OliveTinApiService.Init:output_type -> olivetin.api.v1.InitResponse
63, // 81: olivetin.api.v1.OliveTinApiService.GetActionBinding:output_type -> olivetin.api.v1.GetActionBindingResponse
65, // 82: olivetin.api.v1.OliveTinApiService.GetEntities:output_type -> olivetin.api.v1.GetEntitiesResponse
4, // 83: olivetin.api.v1.OliveTinApiService.GetEntity:output_type -> olivetin.api.v1.Entity
59, // [59:84] is the sub-list for method output_type
34, // [34:59] is the sub-list for method input_type
34, // [34:34] is the sub-list for extension type_name
34, // [34:34] is the sub-list for extension extendee
0, // [0:34] is the sub-list for field type_name
}
func init() { file_olivetin_api_v1_olivetin_proto_init() }
@ -4514,7 +4543,7 @@ func file_olivetin_api_v1_olivetin_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_olivetin_api_v1_olivetin_proto_rawDesc), len(file_olivetin_api_v1_olivetin_proto_rawDesc)),
NumEnums: 0,
NumMessages: 73,
NumMessages: 75,
NumExtensions: 0,
NumServices: 1,
},

View File

@ -18,8 +18,10 @@ func applyActionExecTriggers(pb *apiv1.Action, cfg *config.Action) {
for _, wh := range cfg.ExecOnWebhook {
pb.ExecOnWebhooks = append(pb.ExecOnWebhooks, &apiv1.ActionWebhookExecHint{
Template: wh.Template,
MatchPath: wh.MatchPath,
Template: wh.Template,
MatchPath: wh.MatchPath,
MatchHeaders: wh.MatchHeaders,
MatchQuery: wh.MatchQuery,
})
}
}

View File

@ -52,6 +52,24 @@ func getNewTestServerAndClient(injectedConfig *config.Config) (*httptest.Server,
return ts, client
}
func TestApplyActionExecTriggersIncludesWebhookHeaderAndQueryMatches(t *testing.T) {
cfg := &config.Action{
ExecOnWebhook: []config.WebhookConfig{
{
MatchHeaders: map[string]string{"X-GitHub-Event": "push"},
MatchQuery: map[string]string{"source": "github"},
},
},
}
pb := &apiv1.Action{}
applyActionExecTriggers(pb, cfg)
require.Len(t, pb.ExecOnWebhooks, 1)
assert.Equal(t, cfg.ExecOnWebhook[0].MatchHeaders, pb.ExecOnWebhooks[0].MatchHeaders)
assert.Equal(t, cfg.ExecOnWebhook[0].MatchQuery, pb.ExecOnWebhooks[0].MatchQuery)
}
func TestGetActionsAndStart(t *testing.T) {
cfg := config.DefaultConfig()