Merge branch 'next' into fix-require-guests-login
This commit is contained in:
commit
430aab638b
|
|
@ -15,7 +15,8 @@ import { Mutex } from './Mutex.js'
|
||||||
* occour in sequential order.
|
* occour in sequential order.
|
||||||
*/
|
*/
|
||||||
export class OutputTerminal {
|
export class OutputTerminal {
|
||||||
constructor () {
|
constructor (executionTrackingId) {
|
||||||
|
this.executionTrackingId = executionTrackingId
|
||||||
this.writeMutex = new Mutex()
|
this.writeMutex = new Mutex()
|
||||||
this.terminal = new Terminal({
|
this.terminal = new Terminal({
|
||||||
convertEol: true
|
convertEol: true
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,11 @@ export declare type Action = Message<"olivetin.api.v1.Action"> & {
|
||||||
* @generated from field: int32 order = 7;
|
* @generated from field: int32 order = 7;
|
||||||
*/
|
*/
|
||||||
order: number;
|
order: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @generated from field: int32 timeout = 8;
|
||||||
|
*/
|
||||||
|
timeout: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -581,6 +586,63 @@ export declare type GetLogsResponse = Message<"olivetin.api.v1.GetLogsResponse">
|
||||||
*/
|
*/
|
||||||
export declare const GetLogsResponseSchema: GenMessage<GetLogsResponse>;
|
export declare const GetLogsResponseSchema: GenMessage<GetLogsResponse>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @generated from message olivetin.api.v1.GetActionLogsRequest
|
||||||
|
*/
|
||||||
|
export declare type GetActionLogsRequest = Message<"olivetin.api.v1.GetActionLogsRequest"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: string action_id = 1;
|
||||||
|
*/
|
||||||
|
actionId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @generated from field: int64 start_offset = 2;
|
||||||
|
*/
|
||||||
|
startOffset: bigint;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the message olivetin.api.v1.GetActionLogsRequest.
|
||||||
|
* Use `create(GetActionLogsRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const GetActionLogsRequestSchema: GenMessage<GetActionLogsRequest>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @generated from message olivetin.api.v1.GetActionLogsResponse
|
||||||
|
*/
|
||||||
|
export declare type GetActionLogsResponse = Message<"olivetin.api.v1.GetActionLogsResponse"> & {
|
||||||
|
/**
|
||||||
|
* @generated from field: repeated olivetin.api.v1.LogEntry logs = 1;
|
||||||
|
*/
|
||||||
|
logs: LogEntry[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @generated from field: int64 count_remaining = 2;
|
||||||
|
*/
|
||||||
|
countRemaining: bigint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @generated from field: int64 page_size = 3;
|
||||||
|
*/
|
||||||
|
pageSize: bigint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @generated from field: int64 total_count = 4;
|
||||||
|
*/
|
||||||
|
totalCount: bigint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @generated from field: int64 start_offset = 5;
|
||||||
|
*/
|
||||||
|
startOffset: bigint;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the message olivetin.api.v1.GetActionLogsResponse.
|
||||||
|
* Use `create(GetActionLogsResponseSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export declare const GetActionLogsResponseSchema: GenMessage<GetActionLogsResponse>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @generated from message olivetin.api.v1.ValidateArgumentTypeRequest
|
* @generated from message olivetin.api.v1.ValidateArgumentTypeRequest
|
||||||
*/
|
*/
|
||||||
|
|
@ -1575,6 +1637,14 @@ export declare const OliveTinApiService: GenService<{
|
||||||
input: typeof GetLogsRequestSchema;
|
input: typeof GetLogsRequestSchema;
|
||||||
output: typeof GetLogsResponseSchema;
|
output: typeof GetLogsResponseSchema;
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* @generated from rpc olivetin.api.v1.OliveTinApiService.GetActionLogs
|
||||||
|
*/
|
||||||
|
getActionLogs: {
|
||||||
|
methodKind: "unary";
|
||||||
|
input: typeof GetActionLogsRequestSchema;
|
||||||
|
output: typeof GetActionLogsResponseSchema;
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* @generated from rpc olivetin.api.v1.OliveTinApiService.ValidateArgumentType
|
* @generated from rpc olivetin.api.v1.OliveTinApiService.ValidateArgumentType
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -157,320 +157,334 @@ export const LogEntrySchema = /*@__PURE__*/
|
||||||
export const GetLogsResponseSchema = /*@__PURE__*/
|
export const GetLogsResponseSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 20);
|
messageDesc(file_olivetin_api_v1_olivetin, 20);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the message olivetin.api.v1.GetActionLogsRequest.
|
||||||
|
* Use `create(GetActionLogsRequestSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export const GetActionLogsRequestSchema = /*@__PURE__*/
|
||||||
|
messageDesc(file_olivetin_api_v1_olivetin, 21);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the message olivetin.api.v1.GetActionLogsResponse.
|
||||||
|
* Use `create(GetActionLogsResponseSchema)` to create a new message.
|
||||||
|
*/
|
||||||
|
export const GetActionLogsResponseSchema = /*@__PURE__*/
|
||||||
|
messageDesc(file_olivetin_api_v1_olivetin, 22);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.ValidateArgumentTypeRequest.
|
* Describes the message olivetin.api.v1.ValidateArgumentTypeRequest.
|
||||||
* Use `create(ValidateArgumentTypeRequestSchema)` to create a new message.
|
* Use `create(ValidateArgumentTypeRequestSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const ValidateArgumentTypeRequestSchema = /*@__PURE__*/
|
export const ValidateArgumentTypeRequestSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 21);
|
messageDesc(file_olivetin_api_v1_olivetin, 23);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.ValidateArgumentTypeResponse.
|
* Describes the message olivetin.api.v1.ValidateArgumentTypeResponse.
|
||||||
* Use `create(ValidateArgumentTypeResponseSchema)` to create a new message.
|
* Use `create(ValidateArgumentTypeResponseSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const ValidateArgumentTypeResponseSchema = /*@__PURE__*/
|
export const ValidateArgumentTypeResponseSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 22);
|
messageDesc(file_olivetin_api_v1_olivetin, 24);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.WatchExecutionRequest.
|
* Describes the message olivetin.api.v1.WatchExecutionRequest.
|
||||||
* Use `create(WatchExecutionRequestSchema)` to create a new message.
|
* Use `create(WatchExecutionRequestSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const WatchExecutionRequestSchema = /*@__PURE__*/
|
export const WatchExecutionRequestSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 23);
|
messageDesc(file_olivetin_api_v1_olivetin, 25);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.WatchExecutionUpdate.
|
* Describes the message olivetin.api.v1.WatchExecutionUpdate.
|
||||||
* Use `create(WatchExecutionUpdateSchema)` to create a new message.
|
* Use `create(WatchExecutionUpdateSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const WatchExecutionUpdateSchema = /*@__PURE__*/
|
export const WatchExecutionUpdateSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 24);
|
messageDesc(file_olivetin_api_v1_olivetin, 26);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.ExecutionStatusRequest.
|
* Describes the message olivetin.api.v1.ExecutionStatusRequest.
|
||||||
* Use `create(ExecutionStatusRequestSchema)` to create a new message.
|
* Use `create(ExecutionStatusRequestSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const ExecutionStatusRequestSchema = /*@__PURE__*/
|
export const ExecutionStatusRequestSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 25);
|
messageDesc(file_olivetin_api_v1_olivetin, 27);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.ExecutionStatusResponse.
|
* Describes the message olivetin.api.v1.ExecutionStatusResponse.
|
||||||
* Use `create(ExecutionStatusResponseSchema)` to create a new message.
|
* Use `create(ExecutionStatusResponseSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const ExecutionStatusResponseSchema = /*@__PURE__*/
|
export const ExecutionStatusResponseSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 26);
|
messageDesc(file_olivetin_api_v1_olivetin, 28);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.WhoAmIRequest.
|
* Describes the message olivetin.api.v1.WhoAmIRequest.
|
||||||
* Use `create(WhoAmIRequestSchema)` to create a new message.
|
* Use `create(WhoAmIRequestSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const WhoAmIRequestSchema = /*@__PURE__*/
|
export const WhoAmIRequestSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 27);
|
messageDesc(file_olivetin_api_v1_olivetin, 29);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.WhoAmIResponse.
|
* Describes the message olivetin.api.v1.WhoAmIResponse.
|
||||||
* Use `create(WhoAmIResponseSchema)` to create a new message.
|
* Use `create(WhoAmIResponseSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const WhoAmIResponseSchema = /*@__PURE__*/
|
export const WhoAmIResponseSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 28);
|
messageDesc(file_olivetin_api_v1_olivetin, 30);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.SosReportRequest.
|
* Describes the message olivetin.api.v1.SosReportRequest.
|
||||||
* Use `create(SosReportRequestSchema)` to create a new message.
|
* Use `create(SosReportRequestSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const SosReportRequestSchema = /*@__PURE__*/
|
export const SosReportRequestSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 29);
|
messageDesc(file_olivetin_api_v1_olivetin, 31);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.SosReportResponse.
|
* Describes the message olivetin.api.v1.SosReportResponse.
|
||||||
* Use `create(SosReportResponseSchema)` to create a new message.
|
* Use `create(SosReportResponseSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const SosReportResponseSchema = /*@__PURE__*/
|
export const SosReportResponseSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 30);
|
messageDesc(file_olivetin_api_v1_olivetin, 32);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.DumpVarsRequest.
|
* Describes the message olivetin.api.v1.DumpVarsRequest.
|
||||||
* Use `create(DumpVarsRequestSchema)` to create a new message.
|
* Use `create(DumpVarsRequestSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const DumpVarsRequestSchema = /*@__PURE__*/
|
export const DumpVarsRequestSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 31);
|
messageDesc(file_olivetin_api_v1_olivetin, 33);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.DumpVarsResponse.
|
* Describes the message olivetin.api.v1.DumpVarsResponse.
|
||||||
* Use `create(DumpVarsResponseSchema)` to create a new message.
|
* Use `create(DumpVarsResponseSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const DumpVarsResponseSchema = /*@__PURE__*/
|
export const DumpVarsResponseSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 32);
|
messageDesc(file_olivetin_api_v1_olivetin, 34);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.ActionEntityPair.
|
* Describes the message olivetin.api.v1.ActionEntityPair.
|
||||||
* Use `create(ActionEntityPairSchema)` to create a new message.
|
* Use `create(ActionEntityPairSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const ActionEntityPairSchema = /*@__PURE__*/
|
export const ActionEntityPairSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 33);
|
messageDesc(file_olivetin_api_v1_olivetin, 35);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.DumpPublicIdActionMapRequest.
|
* Describes the message olivetin.api.v1.DumpPublicIdActionMapRequest.
|
||||||
* Use `create(DumpPublicIdActionMapRequestSchema)` to create a new message.
|
* Use `create(DumpPublicIdActionMapRequestSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const DumpPublicIdActionMapRequestSchema = /*@__PURE__*/
|
export const DumpPublicIdActionMapRequestSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 34);
|
messageDesc(file_olivetin_api_v1_olivetin, 36);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.DumpPublicIdActionMapResponse.
|
* Describes the message olivetin.api.v1.DumpPublicIdActionMapResponse.
|
||||||
* Use `create(DumpPublicIdActionMapResponseSchema)` to create a new message.
|
* Use `create(DumpPublicIdActionMapResponseSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const DumpPublicIdActionMapResponseSchema = /*@__PURE__*/
|
export const DumpPublicIdActionMapResponseSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 35);
|
messageDesc(file_olivetin_api_v1_olivetin, 37);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.GetReadyzRequest.
|
* Describes the message olivetin.api.v1.GetReadyzRequest.
|
||||||
* Use `create(GetReadyzRequestSchema)` to create a new message.
|
* Use `create(GetReadyzRequestSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const GetReadyzRequestSchema = /*@__PURE__*/
|
export const GetReadyzRequestSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 36);
|
messageDesc(file_olivetin_api_v1_olivetin, 38);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.GetReadyzResponse.
|
* Describes the message olivetin.api.v1.GetReadyzResponse.
|
||||||
* Use `create(GetReadyzResponseSchema)` to create a new message.
|
* Use `create(GetReadyzResponseSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const GetReadyzResponseSchema = /*@__PURE__*/
|
export const GetReadyzResponseSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 37);
|
messageDesc(file_olivetin_api_v1_olivetin, 39);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.EventStreamRequest.
|
* Describes the message olivetin.api.v1.EventStreamRequest.
|
||||||
* Use `create(EventStreamRequestSchema)` to create a new message.
|
* Use `create(EventStreamRequestSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const EventStreamRequestSchema = /*@__PURE__*/
|
export const EventStreamRequestSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 38);
|
messageDesc(file_olivetin_api_v1_olivetin, 40);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.EventStreamResponse.
|
* Describes the message olivetin.api.v1.EventStreamResponse.
|
||||||
* Use `create(EventStreamResponseSchema)` to create a new message.
|
* Use `create(EventStreamResponseSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const EventStreamResponseSchema = /*@__PURE__*/
|
export const EventStreamResponseSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 39);
|
messageDesc(file_olivetin_api_v1_olivetin, 41);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.EventOutputChunk.
|
* Describes the message olivetin.api.v1.EventOutputChunk.
|
||||||
* Use `create(EventOutputChunkSchema)` to create a new message.
|
* Use `create(EventOutputChunkSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const EventOutputChunkSchema = /*@__PURE__*/
|
export const EventOutputChunkSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 40);
|
messageDesc(file_olivetin_api_v1_olivetin, 42);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.EventEntityChanged.
|
* Describes the message olivetin.api.v1.EventEntityChanged.
|
||||||
* Use `create(EventEntityChangedSchema)` to create a new message.
|
* Use `create(EventEntityChangedSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const EventEntityChangedSchema = /*@__PURE__*/
|
export const EventEntityChangedSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 41);
|
messageDesc(file_olivetin_api_v1_olivetin, 43);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.EventConfigChanged.
|
* Describes the message olivetin.api.v1.EventConfigChanged.
|
||||||
* Use `create(EventConfigChangedSchema)` to create a new message.
|
* Use `create(EventConfigChangedSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const EventConfigChangedSchema = /*@__PURE__*/
|
export const EventConfigChangedSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 42);
|
messageDesc(file_olivetin_api_v1_olivetin, 44);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.EventExecutionFinished.
|
* Describes the message olivetin.api.v1.EventExecutionFinished.
|
||||||
* Use `create(EventExecutionFinishedSchema)` to create a new message.
|
* Use `create(EventExecutionFinishedSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const EventExecutionFinishedSchema = /*@__PURE__*/
|
export const EventExecutionFinishedSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 43);
|
messageDesc(file_olivetin_api_v1_olivetin, 45);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.EventExecutionStarted.
|
* Describes the message olivetin.api.v1.EventExecutionStarted.
|
||||||
* Use `create(EventExecutionStartedSchema)` to create a new message.
|
* Use `create(EventExecutionStartedSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const EventExecutionStartedSchema = /*@__PURE__*/
|
export const EventExecutionStartedSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 44);
|
messageDesc(file_olivetin_api_v1_olivetin, 46);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.KillActionRequest.
|
* Describes the message olivetin.api.v1.KillActionRequest.
|
||||||
* Use `create(KillActionRequestSchema)` to create a new message.
|
* Use `create(KillActionRequestSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const KillActionRequestSchema = /*@__PURE__*/
|
export const KillActionRequestSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 45);
|
messageDesc(file_olivetin_api_v1_olivetin, 47);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.KillActionResponse.
|
* Describes the message olivetin.api.v1.KillActionResponse.
|
||||||
* Use `create(KillActionResponseSchema)` to create a new message.
|
* Use `create(KillActionResponseSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const KillActionResponseSchema = /*@__PURE__*/
|
export const KillActionResponseSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 46);
|
messageDesc(file_olivetin_api_v1_olivetin, 48);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.LocalUserLoginRequest.
|
* Describes the message olivetin.api.v1.LocalUserLoginRequest.
|
||||||
* Use `create(LocalUserLoginRequestSchema)` to create a new message.
|
* Use `create(LocalUserLoginRequestSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const LocalUserLoginRequestSchema = /*@__PURE__*/
|
export const LocalUserLoginRequestSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 47);
|
messageDesc(file_olivetin_api_v1_olivetin, 49);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.LocalUserLoginResponse.
|
* Describes the message olivetin.api.v1.LocalUserLoginResponse.
|
||||||
* Use `create(LocalUserLoginResponseSchema)` to create a new message.
|
* Use `create(LocalUserLoginResponseSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const LocalUserLoginResponseSchema = /*@__PURE__*/
|
export const LocalUserLoginResponseSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 48);
|
messageDesc(file_olivetin_api_v1_olivetin, 50);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.PasswordHashRequest.
|
* Describes the message olivetin.api.v1.PasswordHashRequest.
|
||||||
* Use `create(PasswordHashRequestSchema)` to create a new message.
|
* Use `create(PasswordHashRequestSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const PasswordHashRequestSchema = /*@__PURE__*/
|
export const PasswordHashRequestSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 49);
|
messageDesc(file_olivetin_api_v1_olivetin, 51);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.PasswordHashResponse.
|
* Describes the message olivetin.api.v1.PasswordHashResponse.
|
||||||
* Use `create(PasswordHashResponseSchema)` to create a new message.
|
* Use `create(PasswordHashResponseSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const PasswordHashResponseSchema = /*@__PURE__*/
|
export const PasswordHashResponseSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 50);
|
messageDesc(file_olivetin_api_v1_olivetin, 52);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.LogoutRequest.
|
* Describes the message olivetin.api.v1.LogoutRequest.
|
||||||
* Use `create(LogoutRequestSchema)` to create a new message.
|
* Use `create(LogoutRequestSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const LogoutRequestSchema = /*@__PURE__*/
|
export const LogoutRequestSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 51);
|
messageDesc(file_olivetin_api_v1_olivetin, 53);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.LogoutResponse.
|
* Describes the message olivetin.api.v1.LogoutResponse.
|
||||||
* Use `create(LogoutResponseSchema)` to create a new message.
|
* Use `create(LogoutResponseSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const LogoutResponseSchema = /*@__PURE__*/
|
export const LogoutResponseSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 52);
|
messageDesc(file_olivetin_api_v1_olivetin, 54);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.GetDiagnosticsRequest.
|
* Describes the message olivetin.api.v1.GetDiagnosticsRequest.
|
||||||
* Use `create(GetDiagnosticsRequestSchema)` to create a new message.
|
* Use `create(GetDiagnosticsRequestSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const GetDiagnosticsRequestSchema = /*@__PURE__*/
|
export const GetDiagnosticsRequestSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 53);
|
messageDesc(file_olivetin_api_v1_olivetin, 55);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.GetDiagnosticsResponse.
|
* Describes the message olivetin.api.v1.GetDiagnosticsResponse.
|
||||||
* Use `create(GetDiagnosticsResponseSchema)` to create a new message.
|
* Use `create(GetDiagnosticsResponseSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const GetDiagnosticsResponseSchema = /*@__PURE__*/
|
export const GetDiagnosticsResponseSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 54);
|
messageDesc(file_olivetin_api_v1_olivetin, 56);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.InitRequest.
|
* Describes the message olivetin.api.v1.InitRequest.
|
||||||
* Use `create(InitRequestSchema)` to create a new message.
|
* Use `create(InitRequestSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const InitRequestSchema = /*@__PURE__*/
|
export const InitRequestSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 55);
|
messageDesc(file_olivetin_api_v1_olivetin, 57);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.InitResponse.
|
* Describes the message olivetin.api.v1.InitResponse.
|
||||||
* Use `create(InitResponseSchema)` to create a new message.
|
* Use `create(InitResponseSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const InitResponseSchema = /*@__PURE__*/
|
export const InitResponseSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 56);
|
messageDesc(file_olivetin_api_v1_olivetin, 58);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.AdditionalLink.
|
* Describes the message olivetin.api.v1.AdditionalLink.
|
||||||
* Use `create(AdditionalLinkSchema)` to create a new message.
|
* Use `create(AdditionalLinkSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const AdditionalLinkSchema = /*@__PURE__*/
|
export const AdditionalLinkSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 57);
|
messageDesc(file_olivetin_api_v1_olivetin, 59);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.OAuth2Provider.
|
* Describes the message olivetin.api.v1.OAuth2Provider.
|
||||||
* Use `create(OAuth2ProviderSchema)` to create a new message.
|
* Use `create(OAuth2ProviderSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const OAuth2ProviderSchema = /*@__PURE__*/
|
export const OAuth2ProviderSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 58);
|
messageDesc(file_olivetin_api_v1_olivetin, 60);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.GetActionBindingRequest.
|
* Describes the message olivetin.api.v1.GetActionBindingRequest.
|
||||||
* Use `create(GetActionBindingRequestSchema)` to create a new message.
|
* Use `create(GetActionBindingRequestSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const GetActionBindingRequestSchema = /*@__PURE__*/
|
export const GetActionBindingRequestSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 59);
|
messageDesc(file_olivetin_api_v1_olivetin, 61);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.GetActionBindingResponse.
|
* Describes the message olivetin.api.v1.GetActionBindingResponse.
|
||||||
* Use `create(GetActionBindingResponseSchema)` to create a new message.
|
* Use `create(GetActionBindingResponseSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const GetActionBindingResponseSchema = /*@__PURE__*/
|
export const GetActionBindingResponseSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 60);
|
messageDesc(file_olivetin_api_v1_olivetin, 62);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.GetEntitiesRequest.
|
* Describes the message olivetin.api.v1.GetEntitiesRequest.
|
||||||
* Use `create(GetEntitiesRequestSchema)` to create a new message.
|
* Use `create(GetEntitiesRequestSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const GetEntitiesRequestSchema = /*@__PURE__*/
|
export const GetEntitiesRequestSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 61);
|
messageDesc(file_olivetin_api_v1_olivetin, 63);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.GetEntitiesResponse.
|
* Describes the message olivetin.api.v1.GetEntitiesResponse.
|
||||||
* Use `create(GetEntitiesResponseSchema)` to create a new message.
|
* Use `create(GetEntitiesResponseSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const GetEntitiesResponseSchema = /*@__PURE__*/
|
export const GetEntitiesResponseSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 62);
|
messageDesc(file_olivetin_api_v1_olivetin, 64);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.EntityDefinition.
|
* Describes the message olivetin.api.v1.EntityDefinition.
|
||||||
* Use `create(EntityDefinitionSchema)` to create a new message.
|
* Use `create(EntityDefinitionSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const EntityDefinitionSchema = /*@__PURE__*/
|
export const EntityDefinitionSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 63);
|
messageDesc(file_olivetin_api_v1_olivetin, 65);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.GetEntityRequest.
|
* Describes the message olivetin.api.v1.GetEntityRequest.
|
||||||
* Use `create(GetEntityRequestSchema)` to create a new message.
|
* Use `create(GetEntityRequestSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const GetEntityRequestSchema = /*@__PURE__*/
|
export const GetEntityRequestSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 64);
|
messageDesc(file_olivetin_api_v1_olivetin, 66);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the message olivetin.api.v1.RestartActionRequest.
|
* Describes the message olivetin.api.v1.RestartActionRequest.
|
||||||
* Use `create(RestartActionRequestSchema)` to create a new message.
|
* Use `create(RestartActionRequestSchema)` to create a new message.
|
||||||
*/
|
*/
|
||||||
export const RestartActionRequestSchema = /*@__PURE__*/
|
export const RestartActionRequestSchema = /*@__PURE__*/
|
||||||
messageDesc(file_olivetin_api_v1_olivetin, 65);
|
messageDesc(file_olivetin_api_v1_olivetin, 67);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @generated from service olivetin.api.v1.OliveTinApiService
|
* @generated from service olivetin.api.v1.OliveTinApiService
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,19 @@ const routes = [
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/action/:actionId',
|
||||||
|
name: 'ActionDetails',
|
||||||
|
component: () => import('./views/ActionDetailsView.vue'),
|
||||||
|
props: true,
|
||||||
|
meta: {
|
||||||
|
title: 'Action Details',
|
||||||
|
breadcrumb: [
|
||||||
|
{ name: "Actions", href: "/" },
|
||||||
|
{ name: "Action Details" },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/diagnostics',
|
path: '/diagnostics',
|
||||||
name: 'Diagnostics',
|
name: 'Diagnostics',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,385 @@
|
||||||
|
<template>
|
||||||
|
<Section :title="'Action Details: ' + actionTitle" :padding="false">
|
||||||
|
<template #toolbar>
|
||||||
|
<button v-if="action" @click="startAction" title="Start this action" class="button neutral">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
||||||
|
<path fill="currentColor" d="M8 6v12l8-6z" />
|
||||||
|
</svg>
|
||||||
|
Start
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class = "flex-row padding" v-if="action">
|
||||||
|
<div class = "fg1">
|
||||||
|
<dl>
|
||||||
|
<dt>Title</dt>
|
||||||
|
<dd>{{ action.title }}</dd>
|
||||||
|
<dt>Timeout</dt>
|
||||||
|
<dd>{{ action.timeout }} seconds</dd>
|
||||||
|
</dl>
|
||||||
|
<p v-if="action" class = "fg1">
|
||||||
|
Execution history for this action. You can filter by execution tracking ID.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div style = "align-self: start; text-align: right;">
|
||||||
|
<span class="icon" v-html="action.icon"></span>
|
||||||
|
|
||||||
|
<div class="filter-container">
|
||||||
|
<label class="input-with-icons">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
||||||
|
<path fill="currentColor"
|
||||||
|
d="m19.6 21l-6.3-6.3q-.75.6-1.725.95T9.5 16q-2.725 0-4.612-1.888T3 9.5t1.888-4.612T9.5 3t4.613 1.888T16 9.5q0 1.1-.35 2.075T14.7 13.3l6.3 6.3zM9.5 14q1.875 0 3.188-1.312T14 9.5t-1.312-3.187T9.5 5T6.313 6.313T5 9.5t1.313 3.188T9.5 14" />
|
||||||
|
</svg>
|
||||||
|
<input placeholder="Filter current page" v-model="searchText" />
|
||||||
|
<button title="Clear search filter" :disabled="!searchText" @click="clearSearch">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
||||||
|
<path fill="currentColor"
|
||||||
|
d="M19 6.41L17.59 5L12 10.59L6.41 5L5 6.41L10.59 12L5 17.59L6.41 19L12 13.41L17.59 19L19 17.59L13.41 12z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-show="filteredLogs.length > 0">
|
||||||
|
<table class="logs-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Timestamp</th>
|
||||||
|
<th>Execution ID</th>
|
||||||
|
<th>Metadata</th>
|
||||||
|
<th>Status</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="log in filteredLogs" :key="log.executionTrackingId" class="log-row" :title="log.actionTitle">
|
||||||
|
<td class="timestamp">{{ formatTimestamp(log.datetimeStarted) }}</td>
|
||||||
|
<td>
|
||||||
|
<router-link :to="`/logs/${log.executionTrackingId}`">
|
||||||
|
{{ log.executionTrackingId }}
|
||||||
|
</router-link>
|
||||||
|
</td>
|
||||||
|
<td class="tags">
|
||||||
|
<span class="annotation">
|
||||||
|
<span class="annotation-key">User:</span>
|
||||||
|
<span class="annotation-val">{{ log.user }}</span>
|
||||||
|
</span>
|
||||||
|
<span v-if="log.tags && log.tags.length > 0" class="tag-list">
|
||||||
|
<span v-for="tag in log.tags" :key="tag" class="tag">{{ tag }}</span>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="exit-code">
|
||||||
|
<span :class="getStatusClass(log) + ' annotation'">
|
||||||
|
{{ getStatusText(log) }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<Pagination :pageSize="pageSize" :total="totalCount" :currentPage="currentPage" @page-change="handlePageChange" class="padding"
|
||||||
|
@page-size-change="handlePageSizeChange" itemTitle="execution logs" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-show="logs.length === 0 && !loading" class="empty-state">
|
||||||
|
<p>This action has no execution history.</p>
|
||||||
|
<router-link to="/">Return to index</router-link>
|
||||||
|
</div>
|
||||||
|
</Section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, onMounted, watch } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import Pagination from '../components/Pagination.vue'
|
||||||
|
import Section from 'picocrank/vue/components/Section.vue'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const logs = ref([])
|
||||||
|
const action = ref(null)
|
||||||
|
const actionTitle = ref('Action Details')
|
||||||
|
const searchText = ref('')
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const currentPage = ref(1)
|
||||||
|
const loading = ref(false)
|
||||||
|
const totalCount = ref(0)
|
||||||
|
|
||||||
|
const filteredLogs = computed(() => {
|
||||||
|
if (!searchText.value) {
|
||||||
|
return logs.value
|
||||||
|
}
|
||||||
|
const searchLower = searchText.value.toLowerCase()
|
||||||
|
return logs.value.filter(log =>
|
||||||
|
log.executionTrackingId.toLowerCase().includes(searchLower) ||
|
||||||
|
log.actionTitle.toLowerCase().includes(searchLower)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
async function fetchActionLogs() {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const actionId = route.params.actionId
|
||||||
|
const startOffset = (currentPage.value - 1) * pageSize.value
|
||||||
|
|
||||||
|
const args = {
|
||||||
|
"actionId": actionId,
|
||||||
|
"startOffset": BigInt(startOffset),
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await window.client.getActionLogs(args)
|
||||||
|
|
||||||
|
logs.value = response.logs
|
||||||
|
pageSize.value = Number(response.pageSize) || 0
|
||||||
|
totalCount.value = Number(response.totalCount) || 0
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to fetch action logs:', err)
|
||||||
|
window.showBigError('fetch-action-logs', 'getting action logs', err, false)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchAction() {
|
||||||
|
try {
|
||||||
|
const actionId = route.params.actionId
|
||||||
|
const args = {
|
||||||
|
"bindingId": actionId
|
||||||
|
}
|
||||||
|
const response = await window.client.getActionBinding(args)
|
||||||
|
action.value = response.action
|
||||||
|
actionTitle.value = action.value?.title || 'Unknown Action'
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to fetch action:', err)
|
||||||
|
window.showBigError('fetch-action', 'getting action details', err, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetState() {
|
||||||
|
action.value = null
|
||||||
|
actionTitle.value = 'Action Details'
|
||||||
|
logs.value = []
|
||||||
|
totalCount.value = 0
|
||||||
|
currentPage.value = 1
|
||||||
|
searchText.value = ''
|
||||||
|
loading.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSearch() {
|
||||||
|
searchText.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTimestamp(timestamp) {
|
||||||
|
if (!timestamp) return 'Unknown'
|
||||||
|
try {
|
||||||
|
const date = new Date(timestamp)
|
||||||
|
return date.toLocaleString()
|
||||||
|
} catch (err) {
|
||||||
|
return timestamp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatusClass(log) {
|
||||||
|
if (log.timedOut) return 'status-timeout'
|
||||||
|
if (log.blocked) return 'status-blocked'
|
||||||
|
if (log.exitCode !== 0) return 'status-error'
|
||||||
|
return 'status-success'
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStatusText(log) {
|
||||||
|
if (log.timedOut) return 'Timed out'
|
||||||
|
if (log.blocked) return 'Blocked'
|
||||||
|
if (log.exitCode !== 0) return `Exit code ${log.exitCode}`
|
||||||
|
return 'Completed'
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePageChange(page) {
|
||||||
|
currentPage.value = page
|
||||||
|
fetchActionLogs()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePageSizeChange(newPageSize) {
|
||||||
|
pageSize.value = newPageSize
|
||||||
|
currentPage.value = 1
|
||||||
|
fetchActionLogs()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startAction() {
|
||||||
|
if (!action.value || !action.value.bindingId) {
|
||||||
|
console.error('Cannot start action: no binding ID')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const args = {
|
||||||
|
"bindingId": action.value.bindingId,
|
||||||
|
"arguments": []
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await window.client.startAction(args)
|
||||||
|
router.push(`/logs/${response.executionTrackingId}`)
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to start action:', err)
|
||||||
|
window.showBigError('start-action', 'starting action', err, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchAction()
|
||||||
|
fetchActionLogs()
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.params.actionId,
|
||||||
|
() => {
|
||||||
|
resetState()
|
||||||
|
fetchAction()
|
||||||
|
fetchActionLogs()
|
||||||
|
},
|
||||||
|
{ immediate: false }
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.action-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-header h2 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logs-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logs-table th {
|
||||||
|
background-color: var(--section-background);
|
||||||
|
padding: 0.5rem;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logs-table td {
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-row:hover {
|
||||||
|
background-color: var(--hover-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.timestamp {
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
padding: 2rem;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-with-icons {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
background: var(--section-background);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-with-icons input {
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
background: transparent;
|
||||||
|
flex: 1;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-with-icons button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-with-icons button:disabled {
|
||||||
|
opacity: 0.3;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tags {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.annotation {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.annotation-key {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.annotation-val {
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-list {
|
||||||
|
display: inline-flex;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag {
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
color: var(--accent-text);
|
||||||
|
padding: 0.1rem 0.5rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exit-code .status-success {
|
||||||
|
color: #28a745;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exit-code .status-error {
|
||||||
|
color: #dc3545;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exit-code .status-timeout {
|
||||||
|
color: #ffc107;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exit-code .status-blocked {
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.padding {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
@ -1,7 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<Section :title="'Execution Results: ' + title" id = "execution-results-popup">
|
<Section :title="'Execution Results: ' + title" id = "execution-results-popup">
|
||||||
<template #toolbar>
|
<template #toolbar>
|
||||||
<button @click="toggleSize" title="Toggle dialog size">
|
<router-link v-if="actionId" :to="`/action/${actionId}`" title="View all executions for this action" class="button neutral">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
||||||
|
<path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm.31-8.86c-1.77-.45-2.34-.94-2.34-1.67 0-.84.79-1.43 2.1-1.43 1.38 0 1.9.66 1.94 1.64h1.71c-.05-1.34-.87-2.57-2.49-2.97V5H10.9v1.69c-1.51.32-2.72 1.3-2.72 2.81 0 1.79 1.49 2.69 3.66 3.21 1.95.46 2.34 1.22 2.34 1.8 0 .53-.39 1.39-2.1 1.39-1.6 0-2.05-.56-2.13-1.45H8.04c.08 1.5 1.18 2.37 2.82 2.69V19h2.34v-1.63c1.65-.35 2.48-1.24 2.48-2.77-.01-1.88-1.51-2.87-3.7-3.23z"/>
|
||||||
|
</svg>
|
||||||
|
Action Details
|
||||||
|
</router-link>
|
||||||
|
<button @click="toggleSize" title="Toggle dialog size" class = "neutral">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24">
|
||||||
<path fill="currentColor"
|
<path fill="currentColor"
|
||||||
d="M3 3h6v2H6.462l4.843 4.843l-1.415 1.414L5 6.367V9H3zm0 18h6v-2H6.376l4.929-4.928l-1.415-1.414L5 17.548V15H3zm12 0h6v-6h-2v2.524l-4.867-4.866l-1.414 1.414L17.647 19H15zm6-18h-6v2h2.562l-4.843 4.843l1.414 1.414L19 6.39V9h2z" />
|
d="M3 3h6v2H6.462l4.843 4.843l-1.415 1.414L5 6.367V9H3zm0 18h6v-2H6.376l4.929-4.928l-1.415-1.414L5 17.548V15H3zm12 0h6v-6h-2v2.524l-4.867-4.866l-1.414 1.414L17.647 19H15zm6-18h-6v2h2.562l-4.843 4.843l1.414 1.414L19 6.39V9h2z" />
|
||||||
|
|
@ -22,6 +28,13 @@
|
||||||
<span class="icon" role="img" v-html="icon" style = "align-self: start"></span>
|
<span class="icon" role="img" v-html="icon" style = "align-self: start"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="notFound" class="error-message padded-content">
|
||||||
|
<h3>Execution Not Found</h3>
|
||||||
|
<p>{{ errorMessage }}</p>
|
||||||
|
<p>The execution with ID <code>{{ executionTrackingId }}</code> could not be found.</p>
|
||||||
|
<router-link to="/logs">View all logs</router-link> or <router-link to="/">return to home</router-link>.
|
||||||
|
</div>
|
||||||
|
|
||||||
<div ref="xtermOutput"></div>
|
<div ref="xtermOutput"></div>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
@ -81,12 +94,15 @@ const duration = ref('')
|
||||||
const logEntry = ref(null)
|
const logEntry = ref(null)
|
||||||
const canRerun = ref(false)
|
const canRerun = ref(false)
|
||||||
const canKill = ref(false)
|
const canKill = ref(false)
|
||||||
|
const actionId = ref('')
|
||||||
|
const notFound = ref(false)
|
||||||
|
const errorMessage = ref('')
|
||||||
|
|
||||||
let executionTicker = null
|
let executionTicker = null
|
||||||
let terminal = null
|
let terminal = null
|
||||||
|
|
||||||
function initializeTerminal() {
|
function initializeTerminal() {
|
||||||
terminal = new OutputTerminal(executionTrackingId.value, this)
|
terminal = new OutputTerminal(executionTrackingId.value)
|
||||||
terminal.open(xtermOutput.value)
|
terminal.open(xtermOutput.value)
|
||||||
terminal.resize(80, 24)
|
terminal.resize(80, 24)
|
||||||
|
|
||||||
|
|
@ -94,7 +110,19 @@ function initializeTerminal() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleSize() {
|
function toggleSize() {
|
||||||
terminal.fit()
|
if (!xtermOutput.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xtermOutput.value.requestFullscreen) {
|
||||||
|
xtermOutput.value.requestFullscreen()
|
||||||
|
} else if (xtermOutput.value.webkitRequestFullscreen) {
|
||||||
|
xtermOutput.value.webkitRequestFullscreen()
|
||||||
|
} else if (xtermOutput.value.mozRequestFullScreen) {
|
||||||
|
xtermOutput.value.mozRequestFullScreen()
|
||||||
|
} else if (xtermOutput.value.msRequestFullscreen) {
|
||||||
|
xtermOutput.value.msRequestFullscreen()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function reset() {
|
async function reset() {
|
||||||
|
|
@ -112,6 +140,8 @@ async function reset() {
|
||||||
canRerun.value = false
|
canRerun.value = false
|
||||||
canKill.value = false
|
canKill.value = false
|
||||||
logEntry.value = null
|
logEntry.value = null
|
||||||
|
notFound.value = false
|
||||||
|
errorMessage.value = ''
|
||||||
|
|
||||||
if (terminal) {
|
if (terminal) {
|
||||||
await terminal.reset()
|
await terminal.reset()
|
||||||
|
|
@ -139,10 +169,23 @@ function show(actionButton) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function rerunAction() {
|
async function rerunAction() {
|
||||||
let startActionArgs = {}
|
if (!logEntry.value || !logEntry.value.actionId) {
|
||||||
const res = await window.client.startAction(startActionArgs)
|
console.error('Cannot rerun: no action ID available')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const startActionArgs = {
|
||||||
|
"bindingId": logEntry.value.actionId,
|
||||||
|
"arguments": []
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await window.client.startAction(startActionArgs)
|
||||||
router.push(`/logs/${res.executionTrackingId}`)
|
router.push(`/logs/${res.executionTrackingId}`)
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to rerun action:', err)
|
||||||
|
window.showBigError('rerun-action', 'rerunning action', err, false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function killAction() {
|
async function killAction() {
|
||||||
|
|
@ -177,6 +220,8 @@ async function fetchExecutionResult(executionTrackingIdParam) {
|
||||||
console.log("fetchExecutionResult", executionTrackingIdParam)
|
console.log("fetchExecutionResult", executionTrackingIdParam)
|
||||||
|
|
||||||
executionTrackingId.value = executionTrackingIdParam
|
executionTrackingId.value = executionTrackingIdParam
|
||||||
|
notFound.value = false
|
||||||
|
errorMessage.value = ''
|
||||||
|
|
||||||
const executionStatusArgs = {
|
const executionStatusArgs = {
|
||||||
executionTrackingId: executionTrackingId.value
|
executionTrackingId: executionTrackingId.value
|
||||||
|
|
@ -187,7 +232,13 @@ async function fetchExecutionResult(executionTrackingIdParam) {
|
||||||
|
|
||||||
await renderExecutionResult(logEntryResult)
|
await renderExecutionResult(logEntryResult)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
renderError(err)
|
// Check if it's a "not found" error (404 or similar)
|
||||||
|
if (err.status === 404 || err.code === 'NotFound' || err.message?.includes('not found')) {
|
||||||
|
notFound.value = true
|
||||||
|
errorMessage.value = err.message || 'The execution could not be found in the system.'
|
||||||
|
} else {
|
||||||
|
renderError(err)
|
||||||
|
}
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -204,7 +255,7 @@ function updateDuration(logEntryParam) {
|
||||||
} else {
|
} else {
|
||||||
let delta = ''
|
let delta = ''
|
||||||
try {
|
try {
|
||||||
delta = (new Date(logEntry.value.datetimeStarted) - new Date(logEntry.value.datetimeStarted)) / 1000
|
delta = (new Date(logEntry.value.datetimeFinished) - new Date(logEntry.value.datetimeStarted)) / 1000
|
||||||
delta = new Intl.RelativeTimeFormat().format(delta, 'seconds').replace('in ', '').replace('ago', '')
|
delta = new Intl.RelativeTimeFormat().format(delta, 'seconds').replace('in ', '').replace('ago', '')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('Failed to calculate delta', e)
|
console.warn('Failed to calculate delta', e)
|
||||||
|
|
@ -236,6 +287,7 @@ async function renderExecutionResult(res) {
|
||||||
icon.value = res.logEntry.actionIcon
|
icon.value = res.logEntry.actionIcon
|
||||||
title.value = res.logEntry.actionTitle
|
title.value = res.logEntry.actionTitle
|
||||||
titleTooltip.value = 'Action ID: ' + res.logEntry.actionId + '\nExecution ID: ' + res.logEntry.executionTrackingId
|
titleTooltip.value = 'Action ID: ' + res.logEntry.actionId + '\nExecution ID: ' + res.logEntry.executionTrackingId
|
||||||
|
actionId.value = res.logEntry.actionId
|
||||||
|
|
||||||
updateDuration(res.logEntry)
|
updateDuration(res.logEntry)
|
||||||
|
|
||||||
|
|
@ -308,3 +360,43 @@ defineExpose({
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.action-history-link {
|
||||||
|
color: var(--link-color, #007bff);
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
background-color: #f8d7da;
|
||||||
|
border: 1px solid #f5c2c7;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message h3 {
|
||||||
|
margin: 0 0 0.5rem 0;
|
||||||
|
color: #721c24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message p {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
color: #721c24;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message code {
|
||||||
|
background-color: #f8d7da;
|
||||||
|
padding: 0.125rem 0.25rem;
|
||||||
|
border-radius: 0.125rem;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message a {
|
||||||
|
color: #721c24;
|
||||||
|
text-decoration: underline;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ message Action {
|
||||||
repeated ActionArgument arguments = 5;
|
repeated ActionArgument arguments = 5;
|
||||||
string popup_on_start = 6;
|
string popup_on_start = 6;
|
||||||
int32 order = 7;
|
int32 order = 7;
|
||||||
|
int32 timeout = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ActionArgument {
|
message ActionArgument {
|
||||||
|
|
@ -141,6 +142,19 @@ message GetLogsResponse {
|
||||||
int64 start_offset = 5;
|
int64 start_offset = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message GetActionLogsRequest {
|
||||||
|
string action_id = 1;
|
||||||
|
int64 start_offset = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetActionLogsResponse {
|
||||||
|
repeated LogEntry logs = 1;
|
||||||
|
int64 count_remaining = 2;
|
||||||
|
int64 page_size = 3;
|
||||||
|
int64 total_count = 4;
|
||||||
|
int64 start_offset = 5;
|
||||||
|
}
|
||||||
|
|
||||||
message ValidateArgumentTypeRequest {
|
message ValidateArgumentTypeRequest {
|
||||||
string value = 1;
|
string value = 1;
|
||||||
string type = 2;
|
string type = 2;
|
||||||
|
|
@ -367,6 +381,8 @@ service OliveTinApiService {
|
||||||
rpc ExecutionStatus(ExecutionStatusRequest) returns (ExecutionStatusResponse) {}
|
rpc ExecutionStatus(ExecutionStatusRequest) returns (ExecutionStatusResponse) {}
|
||||||
|
|
||||||
rpc GetLogs(GetLogsRequest) returns (GetLogsResponse) {}
|
rpc GetLogs(GetLogsRequest) returns (GetLogsResponse) {}
|
||||||
|
|
||||||
|
rpc GetActionLogs(GetActionLogsRequest) returns (GetActionLogsResponse) {}
|
||||||
|
|
||||||
rpc ValidateArgumentType(ValidateArgumentTypeRequest) returns (ValidateArgumentTypeResponse) {}
|
rpc ValidateArgumentType(ValidateArgumentTypeRequest) returns (ValidateArgumentTypeResponse) {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,9 @@ const (
|
||||||
// OliveTinApiServiceGetLogsProcedure is the fully-qualified name of the OliveTinApiService's
|
// OliveTinApiServiceGetLogsProcedure is the fully-qualified name of the OliveTinApiService's
|
||||||
// GetLogs RPC.
|
// GetLogs RPC.
|
||||||
OliveTinApiServiceGetLogsProcedure = "/olivetin.api.v1.OliveTinApiService/GetLogs"
|
OliveTinApiServiceGetLogsProcedure = "/olivetin.api.v1.OliveTinApiService/GetLogs"
|
||||||
|
// OliveTinApiServiceGetActionLogsProcedure is the fully-qualified name of the OliveTinApiService's
|
||||||
|
// GetActionLogs RPC.
|
||||||
|
OliveTinApiServiceGetActionLogsProcedure = "/olivetin.api.v1.OliveTinApiService/GetActionLogs"
|
||||||
// OliveTinApiServiceValidateArgumentTypeProcedure is the fully-qualified name of the
|
// OliveTinApiServiceValidateArgumentTypeProcedure is the fully-qualified name of the
|
||||||
// OliveTinApiService's ValidateArgumentType RPC.
|
// OliveTinApiService's ValidateArgumentType RPC.
|
||||||
OliveTinApiServiceValidateArgumentTypeProcedure = "/olivetin.api.v1.OliveTinApiService/ValidateArgumentType"
|
OliveTinApiServiceValidateArgumentTypeProcedure = "/olivetin.api.v1.OliveTinApiService/ValidateArgumentType"
|
||||||
|
|
@ -117,6 +120,7 @@ type OliveTinApiServiceClient interface {
|
||||||
KillAction(context.Context, *connect.Request[v1.KillActionRequest]) (*connect.Response[v1.KillActionResponse], error)
|
KillAction(context.Context, *connect.Request[v1.KillActionRequest]) (*connect.Response[v1.KillActionResponse], error)
|
||||||
ExecutionStatus(context.Context, *connect.Request[v1.ExecutionStatusRequest]) (*connect.Response[v1.ExecutionStatusResponse], error)
|
ExecutionStatus(context.Context, *connect.Request[v1.ExecutionStatusRequest]) (*connect.Response[v1.ExecutionStatusResponse], error)
|
||||||
GetLogs(context.Context, *connect.Request[v1.GetLogsRequest]) (*connect.Response[v1.GetLogsResponse], error)
|
GetLogs(context.Context, *connect.Request[v1.GetLogsRequest]) (*connect.Response[v1.GetLogsResponse], error)
|
||||||
|
GetActionLogs(context.Context, *connect.Request[v1.GetActionLogsRequest]) (*connect.Response[v1.GetActionLogsResponse], error)
|
||||||
ValidateArgumentType(context.Context, *connect.Request[v1.ValidateArgumentTypeRequest]) (*connect.Response[v1.ValidateArgumentTypeResponse], error)
|
ValidateArgumentType(context.Context, *connect.Request[v1.ValidateArgumentTypeRequest]) (*connect.Response[v1.ValidateArgumentTypeResponse], error)
|
||||||
WhoAmI(context.Context, *connect.Request[v1.WhoAmIRequest]) (*connect.Response[v1.WhoAmIResponse], error)
|
WhoAmI(context.Context, *connect.Request[v1.WhoAmIRequest]) (*connect.Response[v1.WhoAmIResponse], error)
|
||||||
SosReport(context.Context, *connect.Request[v1.SosReportRequest]) (*connect.Response[v1.SosReportResponse], error)
|
SosReport(context.Context, *connect.Request[v1.SosReportRequest]) (*connect.Response[v1.SosReportResponse], error)
|
||||||
|
|
@ -199,6 +203,12 @@ func NewOliveTinApiServiceClient(httpClient connect.HTTPClient, baseURL string,
|
||||||
connect.WithSchema(oliveTinApiServiceMethods.ByName("GetLogs")),
|
connect.WithSchema(oliveTinApiServiceMethods.ByName("GetLogs")),
|
||||||
connect.WithClientOptions(opts...),
|
connect.WithClientOptions(opts...),
|
||||||
),
|
),
|
||||||
|
getActionLogs: connect.NewClient[v1.GetActionLogsRequest, v1.GetActionLogsResponse](
|
||||||
|
httpClient,
|
||||||
|
baseURL+OliveTinApiServiceGetActionLogsProcedure,
|
||||||
|
connect.WithSchema(oliveTinApiServiceMethods.ByName("GetActionLogs")),
|
||||||
|
connect.WithClientOptions(opts...),
|
||||||
|
),
|
||||||
validateArgumentType: connect.NewClient[v1.ValidateArgumentTypeRequest, v1.ValidateArgumentTypeResponse](
|
validateArgumentType: connect.NewClient[v1.ValidateArgumentTypeRequest, v1.ValidateArgumentTypeResponse](
|
||||||
httpClient,
|
httpClient,
|
||||||
baseURL+OliveTinApiServiceValidateArgumentTypeProcedure,
|
baseURL+OliveTinApiServiceValidateArgumentTypeProcedure,
|
||||||
|
|
@ -303,6 +313,7 @@ type oliveTinApiServiceClient struct {
|
||||||
killAction *connect.Client[v1.KillActionRequest, v1.KillActionResponse]
|
killAction *connect.Client[v1.KillActionRequest, v1.KillActionResponse]
|
||||||
executionStatus *connect.Client[v1.ExecutionStatusRequest, v1.ExecutionStatusResponse]
|
executionStatus *connect.Client[v1.ExecutionStatusRequest, v1.ExecutionStatusResponse]
|
||||||
getLogs *connect.Client[v1.GetLogsRequest, v1.GetLogsResponse]
|
getLogs *connect.Client[v1.GetLogsRequest, v1.GetLogsResponse]
|
||||||
|
getActionLogs *connect.Client[v1.GetActionLogsRequest, v1.GetActionLogsResponse]
|
||||||
validateArgumentType *connect.Client[v1.ValidateArgumentTypeRequest, v1.ValidateArgumentTypeResponse]
|
validateArgumentType *connect.Client[v1.ValidateArgumentTypeRequest, v1.ValidateArgumentTypeResponse]
|
||||||
whoAmI *connect.Client[v1.WhoAmIRequest, v1.WhoAmIResponse]
|
whoAmI *connect.Client[v1.WhoAmIRequest, v1.WhoAmIResponse]
|
||||||
sosReport *connect.Client[v1.SosReportRequest, v1.SosReportResponse]
|
sosReport *connect.Client[v1.SosReportRequest, v1.SosReportResponse]
|
||||||
|
|
@ -365,6 +376,11 @@ func (c *oliveTinApiServiceClient) GetLogs(ctx context.Context, req *connect.Req
|
||||||
return c.getLogs.CallUnary(ctx, req)
|
return c.getLogs.CallUnary(ctx, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetActionLogs calls olivetin.api.v1.OliveTinApiService.GetActionLogs.
|
||||||
|
func (c *oliveTinApiServiceClient) GetActionLogs(ctx context.Context, req *connect.Request[v1.GetActionLogsRequest]) (*connect.Response[v1.GetActionLogsResponse], error) {
|
||||||
|
return c.getActionLogs.CallUnary(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
// ValidateArgumentType calls olivetin.api.v1.OliveTinApiService.ValidateArgumentType.
|
// ValidateArgumentType calls olivetin.api.v1.OliveTinApiService.ValidateArgumentType.
|
||||||
func (c *oliveTinApiServiceClient) ValidateArgumentType(ctx context.Context, req *connect.Request[v1.ValidateArgumentTypeRequest]) (*connect.Response[v1.ValidateArgumentTypeResponse], error) {
|
func (c *oliveTinApiServiceClient) ValidateArgumentType(ctx context.Context, req *connect.Request[v1.ValidateArgumentTypeRequest]) (*connect.Response[v1.ValidateArgumentTypeResponse], error) {
|
||||||
return c.validateArgumentType.CallUnary(ctx, req)
|
return c.validateArgumentType.CallUnary(ctx, req)
|
||||||
|
|
@ -451,6 +467,7 @@ type OliveTinApiServiceHandler interface {
|
||||||
KillAction(context.Context, *connect.Request[v1.KillActionRequest]) (*connect.Response[v1.KillActionResponse], error)
|
KillAction(context.Context, *connect.Request[v1.KillActionRequest]) (*connect.Response[v1.KillActionResponse], error)
|
||||||
ExecutionStatus(context.Context, *connect.Request[v1.ExecutionStatusRequest]) (*connect.Response[v1.ExecutionStatusResponse], error)
|
ExecutionStatus(context.Context, *connect.Request[v1.ExecutionStatusRequest]) (*connect.Response[v1.ExecutionStatusResponse], error)
|
||||||
GetLogs(context.Context, *connect.Request[v1.GetLogsRequest]) (*connect.Response[v1.GetLogsResponse], error)
|
GetLogs(context.Context, *connect.Request[v1.GetLogsRequest]) (*connect.Response[v1.GetLogsResponse], error)
|
||||||
|
GetActionLogs(context.Context, *connect.Request[v1.GetActionLogsRequest]) (*connect.Response[v1.GetActionLogsResponse], error)
|
||||||
ValidateArgumentType(context.Context, *connect.Request[v1.ValidateArgumentTypeRequest]) (*connect.Response[v1.ValidateArgumentTypeResponse], error)
|
ValidateArgumentType(context.Context, *connect.Request[v1.ValidateArgumentTypeRequest]) (*connect.Response[v1.ValidateArgumentTypeResponse], error)
|
||||||
WhoAmI(context.Context, *connect.Request[v1.WhoAmIRequest]) (*connect.Response[v1.WhoAmIResponse], error)
|
WhoAmI(context.Context, *connect.Request[v1.WhoAmIRequest]) (*connect.Response[v1.WhoAmIResponse], error)
|
||||||
SosReport(context.Context, *connect.Request[v1.SosReportRequest]) (*connect.Response[v1.SosReportResponse], error)
|
SosReport(context.Context, *connect.Request[v1.SosReportRequest]) (*connect.Response[v1.SosReportResponse], error)
|
||||||
|
|
@ -529,6 +546,12 @@ func NewOliveTinApiServiceHandler(svc OliveTinApiServiceHandler, opts ...connect
|
||||||
connect.WithSchema(oliveTinApiServiceMethods.ByName("GetLogs")),
|
connect.WithSchema(oliveTinApiServiceMethods.ByName("GetLogs")),
|
||||||
connect.WithHandlerOptions(opts...),
|
connect.WithHandlerOptions(opts...),
|
||||||
)
|
)
|
||||||
|
oliveTinApiServiceGetActionLogsHandler := connect.NewUnaryHandler(
|
||||||
|
OliveTinApiServiceGetActionLogsProcedure,
|
||||||
|
svc.GetActionLogs,
|
||||||
|
connect.WithSchema(oliveTinApiServiceMethods.ByName("GetActionLogs")),
|
||||||
|
connect.WithHandlerOptions(opts...),
|
||||||
|
)
|
||||||
oliveTinApiServiceValidateArgumentTypeHandler := connect.NewUnaryHandler(
|
oliveTinApiServiceValidateArgumentTypeHandler := connect.NewUnaryHandler(
|
||||||
OliveTinApiServiceValidateArgumentTypeProcedure,
|
OliveTinApiServiceValidateArgumentTypeProcedure,
|
||||||
svc.ValidateArgumentType,
|
svc.ValidateArgumentType,
|
||||||
|
|
@ -639,6 +662,8 @@ func NewOliveTinApiServiceHandler(svc OliveTinApiServiceHandler, opts ...connect
|
||||||
oliveTinApiServiceExecutionStatusHandler.ServeHTTP(w, r)
|
oliveTinApiServiceExecutionStatusHandler.ServeHTTP(w, r)
|
||||||
case OliveTinApiServiceGetLogsProcedure:
|
case OliveTinApiServiceGetLogsProcedure:
|
||||||
oliveTinApiServiceGetLogsHandler.ServeHTTP(w, r)
|
oliveTinApiServiceGetLogsHandler.ServeHTTP(w, r)
|
||||||
|
case OliveTinApiServiceGetActionLogsProcedure:
|
||||||
|
oliveTinApiServiceGetActionLogsHandler.ServeHTTP(w, r)
|
||||||
case OliveTinApiServiceValidateArgumentTypeProcedure:
|
case OliveTinApiServiceValidateArgumentTypeProcedure:
|
||||||
oliveTinApiServiceValidateArgumentTypeHandler.ServeHTTP(w, r)
|
oliveTinApiServiceValidateArgumentTypeHandler.ServeHTTP(w, r)
|
||||||
case OliveTinApiServiceWhoAmIProcedure:
|
case OliveTinApiServiceWhoAmIProcedure:
|
||||||
|
|
@ -714,6 +739,10 @@ func (UnimplementedOliveTinApiServiceHandler) GetLogs(context.Context, *connect.
|
||||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("olivetin.api.v1.OliveTinApiService.GetLogs is not implemented"))
|
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("olivetin.api.v1.OliveTinApiService.GetLogs is not implemented"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (UnimplementedOliveTinApiServiceHandler) GetActionLogs(context.Context, *connect.Request[v1.GetActionLogsRequest]) (*connect.Response[v1.GetActionLogsResponse], error) {
|
||||||
|
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("olivetin.api.v1.OliveTinApiService.GetActionLogs is not implemented"))
|
||||||
|
}
|
||||||
|
|
||||||
func (UnimplementedOliveTinApiServiceHandler) ValidateArgumentType(context.Context, *connect.Request[v1.ValidateArgumentTypeRequest]) (*connect.Response[v1.ValidateArgumentTypeResponse], error) {
|
func (UnimplementedOliveTinApiServiceHandler) ValidateArgumentType(context.Context, *connect.Request[v1.ValidateArgumentTypeRequest]) (*connect.Response[v1.ValidateArgumentTypeResponse], error) {
|
||||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("olivetin.api.v1.OliveTinApiService.ValidateArgumentType is not implemented"))
|
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("olivetin.api.v1.OliveTinApiService.ValidateArgumentType is not implemented"))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -14,6 +14,7 @@ import (
|
||||||
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
acl "github.com/OliveTin/OliveTin/internal/acl"
|
acl "github.com/OliveTin/OliveTin/internal/acl"
|
||||||
auth "github.com/OliveTin/OliveTin/internal/auth"
|
auth "github.com/OliveTin/OliveTin/internal/auth"
|
||||||
|
|
@ -28,10 +29,26 @@ type oliveTinAPI struct {
|
||||||
executor *executor.Executor
|
executor *executor.Executor
|
||||||
cfg *config.Config
|
cfg *config.Config
|
||||||
|
|
||||||
connectedClients []*connectedClients
|
// streamingClients is a set of currently connected clients.
|
||||||
|
// The empty struct value models set semantics (keys only) and keeps add/remove O(1).
|
||||||
|
// We use a map for efficient membership and deletion; ordering is not required.
|
||||||
|
streamingClients map[*streamingClient]struct{}
|
||||||
|
streamingClientsMutex sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
type connectedClients struct {
|
// This is used to avoid race conditions when iterating over the connectedClients map.
|
||||||
|
// and holds the lock for as minimal time as possible to avoid blocking the API for too long.
|
||||||
|
func (api *oliveTinAPI) copyOfStreamingClients() []*streamingClient {
|
||||||
|
api.streamingClientsMutex.RLock()
|
||||||
|
defer api.streamingClientsMutex.RUnlock()
|
||||||
|
clients := make([]*streamingClient, 0, len(api.streamingClients))
|
||||||
|
for client := range api.streamingClients {
|
||||||
|
clients = append(clients, client)
|
||||||
|
}
|
||||||
|
return clients
|
||||||
|
}
|
||||||
|
|
||||||
|
type streamingClient struct {
|
||||||
channel chan *apiv1.EventStreamResponse
|
channel chan *apiv1.EventStreamResponse
|
||||||
AuthenticatedUser *acl.AuthenticatedUser
|
AuthenticatedUser *acl.AuthenticatedUser
|
||||||
}
|
}
|
||||||
|
|
@ -365,6 +382,10 @@ func (api *oliveTinAPI) GetActionBinding(ctx ctx.Context, req *connect.Request[a
|
||||||
|
|
||||||
binding := api.executor.FindBindingByID(req.Msg.BindingId)
|
binding := api.executor.FindBindingByID(req.Msg.BindingId)
|
||||||
|
|
||||||
|
if binding == nil {
|
||||||
|
return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("action with ID %s not found", req.Msg.BindingId))
|
||||||
|
}
|
||||||
|
|
||||||
return connect.NewResponse(&apiv1.GetActionBindingResponse{
|
return connect.NewResponse(&apiv1.GetActionBindingResponse{
|
||||||
Action: buildAction(binding, &DashboardRenderRequest{
|
Action: buildAction(binding, &DashboardRenderRequest{
|
||||||
cfg: api.cfg,
|
cfg: api.cfg,
|
||||||
|
|
@ -436,9 +457,14 @@ func (api *oliveTinAPI) GetLogs(ctx ctx.Context, req *connect.Request[apiv1.GetL
|
||||||
logEntries, pagingResult := api.executor.GetLogTrackingIds(req.Msg.StartOffset, api.cfg.LogHistoryPageSize)
|
logEntries, pagingResult := api.executor.GetLogTrackingIds(req.Msg.StartOffset, api.cfg.LogHistoryPageSize)
|
||||||
|
|
||||||
for _, logEntry := range logEntries {
|
for _, logEntry := range logEntries {
|
||||||
|
// Skip if binding is nil or action is nil
|
||||||
|
if logEntry.Binding == nil || logEntry.Binding.Action == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
action := logEntry.Binding.Action
|
action := logEntry.Binding.Action
|
||||||
|
|
||||||
if action == nil || acl.IsAllowedLogs(api.cfg, user, action) {
|
if acl.IsAllowedLogs(api.cfg, user, action) {
|
||||||
pbLogEntry := api.internalLogEntryToPb(logEntry, user)
|
pbLogEntry := api.internalLogEntryToPb(logEntry, user)
|
||||||
|
|
||||||
ret.Logs = append(ret.Logs, pbLogEntry)
|
ret.Logs = append(ret.Logs, pbLogEntry)
|
||||||
|
|
@ -453,6 +479,75 @@ func (api *oliveTinAPI) GetLogs(ctx ctx.Context, req *connect.Request[apiv1.GetL
|
||||||
return connect.NewResponse(ret), nil
|
return connect.NewResponse(ret), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *oliveTinAPI) GetActionLogs(ctx ctx.Context, req *connect.Request[apiv1.GetActionLogsRequest]) (*connect.Response[apiv1.GetActionLogsResponse], error) {
|
||||||
|
user := acl.UserFromContext(ctx, req, api.cfg)
|
||||||
|
|
||||||
|
if err := api.checkDashboardAccess(user); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := &apiv1.GetActionLogsResponse{}
|
||||||
|
|
||||||
|
logs := api.executor.GetLogsByActionId(req.Msg.ActionId)
|
||||||
|
|
||||||
|
// Apply ACL filtering
|
||||||
|
filteredLogs := make([]*executor.InternalLogEntry, 0)
|
||||||
|
for _, logEntry := range logs {
|
||||||
|
// Skip if binding is nil or action is nil
|
||||||
|
if logEntry.Binding == nil || logEntry.Binding.Action == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
action := logEntry.Binding.Action
|
||||||
|
if acl.IsAllowedLogs(api.cfg, user, action) {
|
||||||
|
filteredLogs = append(filteredLogs, logEntry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pagination
|
||||||
|
totalCount := int64(len(filteredLogs))
|
||||||
|
pageSize := api.cfg.LogHistoryPageSize
|
||||||
|
startOffset := req.Msg.StartOffset
|
||||||
|
|
||||||
|
// Validate and clamp offset to prevent out-of-bounds access
|
||||||
|
if startOffset < 0 {
|
||||||
|
startOffset = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// If offset is beyond available data, return empty result with correct metadata
|
||||||
|
if startOffset >= totalCount {
|
||||||
|
ret.CountRemaining = 0
|
||||||
|
ret.PageSize = pageSize
|
||||||
|
ret.TotalCount = totalCount
|
||||||
|
ret.StartOffset = startOffset
|
||||||
|
return connect.NewResponse(ret), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
startIdx := startOffset
|
||||||
|
endIdx := startOffset + pageSize
|
||||||
|
if endIdx > totalCount {
|
||||||
|
endIdx = totalCount
|
||||||
|
}
|
||||||
|
|
||||||
|
logEntries := filteredLogs[startIdx:endIdx]
|
||||||
|
countRemaining := totalCount - endIdx
|
||||||
|
if countRemaining < 0 {
|
||||||
|
countRemaining = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, logEntry := range logEntries {
|
||||||
|
pbLogEntry := api.internalLogEntryToPb(logEntry, user)
|
||||||
|
ret.Logs = append(ret.Logs, pbLogEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.CountRemaining = countRemaining
|
||||||
|
ret.PageSize = pageSize
|
||||||
|
ret.TotalCount = totalCount
|
||||||
|
ret.StartOffset = startOffset
|
||||||
|
|
||||||
|
return connect.NewResponse(ret), nil
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This function is ONLY a helper for the UI - the arguments are validated properly
|
This function is ONLY a helper for the UI - the arguments are validated properly
|
||||||
on the StartAction -> Executor chain. This is here basically to provide helpful
|
on the StartAction -> Executor chain. This is here basically to provide helpful
|
||||||
|
|
@ -565,20 +660,25 @@ func (api *oliveTinAPI) EventStream(ctx ctx.Context, req *connect.Request[apiv1.
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
client := &connectedClients{
|
client := &streamingClient{
|
||||||
channel: make(chan *apiv1.EventStreamResponse, 10), // Buffered channel to hold Events
|
channel: make(chan *apiv1.EventStreamResponse, 10), // Buffered channel to hold Events
|
||||||
AuthenticatedUser: user,
|
AuthenticatedUser: user,
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("EventStream: client connected: %v", client.AuthenticatedUser.Username)
|
log.Infof("EventStream: client connected: %v", client.AuthenticatedUser.Username)
|
||||||
|
|
||||||
api.connectedClients = append(api.connectedClients, client)
|
api.streamingClientsMutex.Lock()
|
||||||
|
api.streamingClients[client] = struct{}{}
|
||||||
|
api.streamingClientsMutex.Unlock()
|
||||||
|
|
||||||
// loop over client channel and send events to connectedClient
|
// loop over client channel and send events to connectedClient
|
||||||
for msg := range client.channel {
|
for msg := range client.channel {
|
||||||
log.Debugf("Sending event to client: %v", msg)
|
log.Debugf("Sending event to client: %v", msg)
|
||||||
if err := srv.Send(msg); err != nil {
|
if err := srv.Send(msg); err != nil {
|
||||||
log.Errorf("Error sending event to client: %v", err)
|
log.Errorf("Error sending event to client: %v", err)
|
||||||
|
// Remove disconnected client from the list
|
||||||
|
api.removeClient(client)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -587,8 +687,16 @@ func (api *oliveTinAPI) EventStream(ctx ctx.Context, req *connect.Request[apiv1.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *oliveTinAPI) removeClient(clientToRemove *streamingClient) {
|
||||||
|
api.streamingClientsMutex.Lock()
|
||||||
|
delete(api.streamingClients, clientToRemove)
|
||||||
|
api.streamingClientsMutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
func (api *oliveTinAPI) OnActionMapRebuilt() {
|
func (api *oliveTinAPI) OnActionMapRebuilt() {
|
||||||
for _, client := range api.connectedClients {
|
toRemove := []*streamingClient{}
|
||||||
|
|
||||||
|
for _, client := range api.copyOfStreamingClients() {
|
||||||
select {
|
select {
|
||||||
case client.channel <- &apiv1.EventStreamResponse{
|
case client.channel <- &apiv1.EventStreamResponse{
|
||||||
Event: &apiv1.EventStreamResponse_ConfigChanged{
|
Event: &apiv1.EventStreamResponse_ConfigChanged{
|
||||||
|
|
@ -596,13 +704,20 @@ func (api *oliveTinAPI) OnActionMapRebuilt() {
|
||||||
},
|
},
|
||||||
}:
|
}:
|
||||||
default:
|
default:
|
||||||
log.Warnf("EventStream: client channel is full, dropping message")
|
log.Warnf("EventStream: client channel is full, removing client")
|
||||||
|
toRemove = append(toRemove, client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, client := range toRemove {
|
||||||
|
api.removeClient(client)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *oliveTinAPI) OnExecutionStarted(ex *executor.InternalLogEntry) {
|
func (api *oliveTinAPI) OnExecutionStarted(ex *executor.InternalLogEntry) {
|
||||||
for _, client := range api.connectedClients {
|
toRemove := []*streamingClient{}
|
||||||
|
|
||||||
|
for _, client := range api.copyOfStreamingClients() {
|
||||||
select {
|
select {
|
||||||
case client.channel <- &apiv1.EventStreamResponse{
|
case client.channel <- &apiv1.EventStreamResponse{
|
||||||
Event: &apiv1.EventStreamResponse_ExecutionStarted{
|
Event: &apiv1.EventStreamResponse_ExecutionStarted{
|
||||||
|
|
@ -612,13 +727,20 @@ func (api *oliveTinAPI) OnExecutionStarted(ex *executor.InternalLogEntry) {
|
||||||
},
|
},
|
||||||
}:
|
}:
|
||||||
default:
|
default:
|
||||||
log.Warnf("EventStream: client channel is full, dropping message")
|
log.Warnf("EventStream: client channel is full, removing client")
|
||||||
|
toRemove = append(toRemove, client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, client := range toRemove {
|
||||||
|
api.removeClient(client)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *oliveTinAPI) OnExecutionFinished(ex *executor.InternalLogEntry) {
|
func (api *oliveTinAPI) OnExecutionFinished(ex *executor.InternalLogEntry) {
|
||||||
for _, client := range api.connectedClients {
|
toRemove := []*streamingClient{}
|
||||||
|
|
||||||
|
for _, client := range api.copyOfStreamingClients() {
|
||||||
select {
|
select {
|
||||||
case client.channel <- &apiv1.EventStreamResponse{
|
case client.channel <- &apiv1.EventStreamResponse{
|
||||||
Event: &apiv1.EventStreamResponse_ExecutionFinished{
|
Event: &apiv1.EventStreamResponse_ExecutionFinished{
|
||||||
|
|
@ -628,9 +750,14 @@ func (api *oliveTinAPI) OnExecutionFinished(ex *executor.InternalLogEntry) {
|
||||||
},
|
},
|
||||||
}:
|
}:
|
||||||
default:
|
default:
|
||||||
log.Warnf("EventStream: client channel is full, dropping message")
|
log.Warnf("EventStream: client channel is full, removing client")
|
||||||
|
toRemove = append(toRemove, client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, client := range toRemove {
|
||||||
|
api.removeClient(client)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *oliveTinAPI) GetDiagnostics(ctx ctx.Context, req *connect.Request[apiv1.GetDiagnosticsRequest]) (*connect.Response[apiv1.GetDiagnosticsResponse], error) {
|
func (api *oliveTinAPI) GetDiagnostics(ctx ctx.Context, req *connect.Request[apiv1.GetDiagnosticsRequest]) (*connect.Response[apiv1.GetDiagnosticsResponse], error) {
|
||||||
|
|
@ -733,7 +860,9 @@ func buildAdditionalLinks(links []*config.NavigationLink) []*apiv1.AdditionalLin
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *oliveTinAPI) OnOutputChunk(content []byte, executionTrackingId string) {
|
func (api *oliveTinAPI) OnOutputChunk(content []byte, executionTrackingId string) {
|
||||||
for _, client := range api.connectedClients {
|
toRemove := []*streamingClient{}
|
||||||
|
|
||||||
|
for _, client := range api.copyOfStreamingClients() {
|
||||||
select {
|
select {
|
||||||
case client.channel <- &apiv1.EventStreamResponse{
|
case client.channel <- &apiv1.EventStreamResponse{
|
||||||
Event: &apiv1.EventStreamResponse_OutputChunk{
|
Event: &apiv1.EventStreamResponse_OutputChunk{
|
||||||
|
|
@ -744,9 +873,14 @@ func (api *oliveTinAPI) OnOutputChunk(content []byte, executionTrackingId string
|
||||||
},
|
},
|
||||||
}:
|
}:
|
||||||
default:
|
default:
|
||||||
log.Warnf("EventStream: client channel is full, dropping message")
|
log.Warnf("EventStream: client channel is full, removing client")
|
||||||
|
toRemove = append(toRemove, client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, client := range toRemove {
|
||||||
|
api.removeClient(client)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *oliveTinAPI) GetEntities(ctx ctx.Context, req *connect.Request[apiv1.GetEntitiesRequest]) (*connect.Response[apiv1.GetEntitiesResponse], error) {
|
func (api *oliveTinAPI) GetEntities(ctx ctx.Context, req *connect.Request[apiv1.GetEntitiesRequest]) (*connect.Response[apiv1.GetEntitiesResponse], error) {
|
||||||
|
|
@ -863,6 +997,7 @@ func newServer(ex *executor.Executor) *oliveTinAPI {
|
||||||
server := oliveTinAPI{}
|
server := oliveTinAPI{}
|
||||||
server.cfg = ex.Cfg
|
server.cfg = ex.Cfg
|
||||||
server.executor = ex
|
server.executor = ex
|
||||||
|
server.streamingClients = make(map[*streamingClient]struct{})
|
||||||
|
|
||||||
ex.AddListener(&server)
|
ex.AddListener(&server)
|
||||||
return &server
|
return &server
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ func buildAction(actionBinding *executor.ActionBinding, rr *DashboardRenderReque
|
||||||
CanExec: acl.IsAllowedExec(rr.cfg, rr.AuthenticatedUser, action),
|
CanExec: acl.IsAllowedExec(rr.cfg, rr.AuthenticatedUser, action),
|
||||||
PopupOnStart: action.PopupOnStart,
|
PopupOnStart: action.PopupOnStart,
|
||||||
Order: int32(actionBinding.ConfigOrder),
|
Order: int32(actionBinding.ConfigOrder),
|
||||||
|
Timeout: int32(action.Timeout),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, cfgArg := range action.Arguments {
|
for _, cfgArg := range action.Arguments {
|
||||||
|
|
|
||||||
|
|
@ -482,6 +482,7 @@ func stepRequestAction(req *ExecutionRequest) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
req.logEntry.Binding = req.Binding
|
||||||
req.logEntry.ActionConfigTitle = req.Binding.Action.Title
|
req.logEntry.ActionConfigTitle = req.Binding.Action.Title
|
||||||
req.logEntry.ActionTitle = entities.ParseTemplateWith(req.Binding.Action.Title, req.Binding.Entity)
|
req.logEntry.ActionTitle = entities.ParseTemplateWith(req.Binding.Action.Title, req.Binding.Entity)
|
||||||
req.logEntry.ActionIcon = req.Binding.Action.Icon
|
req.logEntry.ActionIcon = req.Binding.Action.Icon
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue