diff --git a/CLAUDE.md b/CLAUDE.md index 451c905..4cb2558 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,12 +9,12 @@ LAPP (Log Auto Pattern Pipeline) discovers log templates from log streams using ## Commands ```bash -make build # Build embedded frontend assets, then output/lapp -make clean # Remove generated build artifacts -make dev # Clean, build, and start lapp web on 127.0.0.1:8080 -make proto-gen # Generate protobuf/Connect code -make test # Run unit and integration tests -make check # Run formatting, linting, type checks, build, and unit tests +make build # Build embedded frontend assets, then output/lapp +make clean # Remove generated build artifacts +make dev # Clean, build, and start lapp web on 127.0.0.1:8080 +make proto-gen # Generate protobuf/Connect code +make test # Run unit and integration tests +make check # Run formatting, linting, type checks, build, and unit tests # Run a single test go test -v -run TestFunctionName ./pkg/pattern/ @@ -24,10 +24,11 @@ go test -v -run TestFunctionName ./pkg/pattern/ ```bash go run ./cmd/lapp/ workspace create -go run ./cmd/lapp/ workspace add-log --topic [--model ] -go run ./cmd/lapp/ workspace add-log --topic --stdin [--model ] -go run ./cmd/lapp/ workspace analyze --topic [question] [--model ] -go run ./cmd/lapp/ web [--addr 127.0.0.1:0] +go run ./cmd/lapp/ workspace add-log --topic [--model ] +go run ./cmd/lapp/ workspace add-log --topic --stdin [--model ] +go run ./cmd/lapp/ workspace add-log --topic --gcp-project [--gcp-filter ] [--since 1h] [--limit 10000] +go run ./cmd/lapp/ workspace analyze --topic [question] [--model ] +go run ./cmd/lapp/ web [--addr 127.0.0.1:0] ``` Topic names are sanitized to lower-kebab-case. Workspaces live under `~/.lapp/workspaces//`. @@ -37,33 +38,34 @@ Topic names are sanitized to lower-kebab-case. Workspaces live under `~/.lapp/wo ``` cmd/lapp/ CLI entrypoint (cobra commands: workspace create/add-log/analyze) pkg/logsource/ Read log files → channel of LogLine +pkg/gcplog/ Fetch Google Cloud Logging entries → text log lines pkg/multiline/ Detect log entry boundaries, merge continuation lines pkg/pattern/ Drain-based log pattern discovery and template matching pkg/semantic/ LLM-based semantic labeling of Drain patterns -pkg/workspace/ DiscoveryRun execution and run-scoped file writer -pkg/store/ DuckDB storage primitives (not yet on the CLI add-log path) +pkg/workspace/ DiscoveryRun execution and run-scoped file writer +pkg/store/ DuckDB storage primitives (not yet on the CLI add-log path) pkg/config/ Model resolution (flag → $MODEL_NAME → default) -pkg/analyzer/ Agentic log analysis via eino ADK + ACP providers +pkg/analyzer/ Agentic log analysis via eino ADK + ACP providers integration_test/ Integration tests against Loghub-2.0 datasets ``` -### DiscoveryRun (add-log) - -Each `add-log` copies a log file, then starts a DiscoveryRun: reads ALL files in `logs/`, runs fresh Drain + semantic labeling, and writes run-scoped `patterns/` and `notes/`. -When `lapp web` starts, it marks any previous `QUEUED` or `RUNNING` DiscoveryRuns as failed because those local workers no longer exist. -DiscoveryRun records persist structured `progress` and `error` fields; frontend code renders those facts into user-facing text. - -``` -workspace.Discover(ctx, cfg) - → Read all logs/ files → multiline.MergeSlice() per file → tagged lines - → pattern.DrainParser.Feed(all content) → Templates() → filter Count > 1 - → semantic.Label(ctx, cfg, patterns) ← LLM batches with per-batch retry - → workspace.NewBuilder(...).BuildAll() - → discovery-runs//patterns//pattern.md + samples.log - → discovery-runs//patterns/unmatched/samples.log - → discovery-runs//notes/summary.md + errors.md - → discovery-runs//AGENTS.md -``` +### DiscoveryRun (add-log) + +Each `add-log` copies a log file, then starts a DiscoveryRun: reads ALL files in `logs/`, runs fresh Drain + semantic labeling, and writes run-scoped `patterns/` and `notes/`. +When `lapp web` starts, it marks any previous `QUEUED` or `RUNNING` DiscoveryRuns as failed because those local workers no longer exist. +DiscoveryRun records persist structured `progress` and `error` fields; frontend code renders those facts into user-facing text. + +``` +workspace.Discover(ctx, cfg) + → Read all logs/ files → multiline.MergeSlice() per file → tagged lines + → pattern.DrainParser.Feed(all content) → Templates() → filter Count > 1 + → semantic.Label(ctx, cfg, patterns) ← LLM batches with per-batch retry + → workspace.NewBuilder(...).BuildAll() + → discovery-runs//patterns//pattern.md + samples.log + → discovery-runs//patterns/unmatched/samples.log + → discovery-runs//notes/summary.md + errors.md + → discovery-runs//AGENTS.md +``` ### Multiline Detection @@ -75,14 +77,15 @@ Runs an eino ADK agent (15 max iterations) with filesystem tools (grep, read_fil ## Environment Variables -- `OPENROUTER_API_KEY`: Required for semantic labeling in `workspace add-log` -- `MODEL_NAME`: Override default LLM model (default: `google/gemini-3-flash-preview`) -- ACP provider credentials/login: Required for `workspace analyze` through the selected provider -- `.env` file is auto-loaded via godotenv +- `OPENROUTER_API_KEY`: Required for semantic labeling in `workspace add-log` +- `MODEL_NAME`: Override default LLM model (default: `google/gemini-3-flash-preview`) +- ACP provider credentials/login: Required for `workspace analyze` through the selected provider +- Google Application Default Credentials: Required for GCP log import (`add-log --gcp-project` and the web ImportLogs RPC); run `gcloud auth application-default login` +- `.env` file is auto-loaded via godotenv ## Tech Stack -- Go, cobra CLI, go-drain3, DuckDB (duckdb-go/v2), cloudwego/eino ADK, OpenRouter semantic labeling, ACP providers +- Go, cobra CLI, go-drain3, DuckDB (duckdb-go/v2), cloudwego/eino ADK, OpenRouter semantic labeling, ACP providers ## Code Style diff --git a/cmd/lapp/workspace.go b/cmd/lapp/workspace.go index 6d7a627..581f679 100644 --- a/cmd/lapp/workspace.go +++ b/cmd/lapp/workspace.go @@ -1,6 +1,7 @@ package main import ( + "context" "fmt" "io" "log/slog" @@ -13,6 +14,7 @@ import ( "github.com/go-errors/errors" "github.com/spf13/cobra" "github.com/strrl/lapp/pkg/analyzer" + "github.com/strrl/lapp/pkg/gcplog" "github.com/strrl/lapp/pkg/workspace" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -149,6 +151,10 @@ Use ` + "`lapp workspace add-log --topic " + topic + " `" + ` to add lo var addLogModel string var addLogStdin bool var addLogTopic string +var addLogGCPProject string +var addLogGCPFilter string +var addLogGCPSince time.Duration +var addLogGCPLimit int func workspaceAddLogCmd() *cobra.Command { cmd := &cobra.Command{ @@ -157,6 +163,9 @@ func workspaceAddLogCmd() *cobra.Command { Long: `Copy a log file into the workspace's logs/ directory, then run the full DiscoveryRun flow (Drain clustering + semantic labeling) to generate run-scoped results. +Logs can come from a file, stdin, or Google Cloud Logging (--gcp-project, +requires Application Default Credentials). + Requires OPENROUTER_API_KEY environment variable.`, Args: cobra.MaximumNArgs(1), RunE: runWorkspaceAddLog, @@ -164,6 +173,10 @@ Requires OPENROUTER_API_KEY environment variable.`, cmd.Flags().StringVar(&addLogTopic, "topic", "", "workspace topic (required)") cmd.Flags().StringVar(&addLogModel, "model", "", "override LLM model") cmd.Flags().BoolVar(&addLogStdin, "stdin", false, "read log from stdin") + cmd.Flags().StringVar(&addLogGCPProject, "gcp-project", "", "import logs from this Google Cloud project") + cmd.Flags().StringVar(&addLogGCPFilter, "gcp-filter", "", "Cloud Logging filter expression") + cmd.Flags().DurationVar(&addLogGCPSince, "since", time.Hour, "how far back to fetch Cloud Logging entries") + cmd.Flags().IntVar(&addLogGCPLimit, "limit", 10000, "maximum number of Cloud Logging entries to fetch") _ = cmd.MarkFlagRequired("topic") return cmd } @@ -188,7 +201,7 @@ func runWorkspaceAddLog(cmd *cobra.Command, args []string) error { ctx, span := otel.Tracer("lapp/cmd").Start(cmd.Context(), "cmd.WorkspaceAddLog") defer span.End() - if err := copyLogToWorkspace(dir, args, span.SetAttributes); err != nil { + if err := copyLogToWorkspace(ctx, dir, args, span.SetAttributes); err != nil { return err } @@ -206,7 +219,17 @@ func runWorkspaceAddLog(cmd *cobra.Command, args []string) error { return nil } -func copyLogToWorkspace(dir string, args []string, setSpanAttributes func(...attribute.KeyValue)) error { +func copyLogToWorkspace(ctx context.Context, dir string, args []string, setSpanAttributes func(...attribute.KeyValue)) error { + if addLogGCPProject != "" { + if addLogStdin { + return errors.New("--stdin and --gcp-project are mutually exclusive") + } + if len(args) > 0 { + return errors.New("logfile argument and --gcp-project are mutually exclusive") + } + return importGCPLogs(ctx, dir, setSpanAttributes) + } + if addLogStdin { name := fmt.Sprintf("stdin-%d.log", time.Now().UnixNano()) data, err := io.ReadAll(os.Stdin) @@ -238,6 +261,45 @@ func copyLogToWorkspace(dir string, args []string, setSpanAttributes func(...att return nil } +// importGCPLogs fetches Cloud Logging entries into the workspace logs +// directory as a regular log file. +func importGCPLogs(ctx context.Context, dir string, setSpanAttributes func(...attribute.KeyValue)) error { + setSpanAttributes(attribute.String("gcp.project", addLogGCPProject)) + + name := fmt.Sprintf("gcp-%s-%s.log", addLogGCPProject, time.Now().UTC().Format("20060102-150405")) + path := filepath.Join(dir, "logs", name) + file, err := os.Create(path) + if err != nil { + return errors.Errorf("create import log file: %w", err) + } + + count, fetchErr := gcplog.NewFetcher().Fetch(ctx, gcplog.FetchConfig{ + ProjectID: addLogGCPProject, + Filter: addLogGCPFilter, + Since: time.Now().Add(-addLogGCPSince), + Limit: addLogGCPLimit, + OnProgress: func(fetched int) { + slog.Info("Fetching GCP logs", "entries", fetched) + }, + }, file) + closeErr := file.Close() + if fetchErr != nil || closeErr != nil || count == 0 { + _ = os.Remove(path) + } + if fetchErr != nil { + return errors.Errorf("fetch gcp logs: %w", fetchErr) + } + if closeErr != nil { + return errors.Errorf("close import log file: %w", closeErr) + } + if count == 0 { + return errors.New("no log entries matched the filter and time range") + } + + slog.Info("Imported GCP logs", "name", name, "entries", count) + return nil +} + var analyzeWsModel string var analyzeWsACP string var analyzeTopic string diff --git a/frontend/src/gen/lapp/event/v1/event_pb.ts b/frontend/src/gen/lapp/event/v1/event_pb.ts index dbeee81..3ebf6ea 100644 --- a/frontend/src/gen/lapp/event/v1/event_pb.ts +++ b/frontend/src/gen/lapp/event/v1/event_pb.ts @@ -1,4 +1,4 @@ -// @generated by protoc-gen-es v2.12.0 with parameter "target=ts" +// @generated by protoc-gen-es v2.12.1 with parameter "target=ts" // @generated from file lapp/event/v1/event.proto (package lapp.event.v1, syntax proto3) /* eslint-disable */ diff --git a/frontend/src/gen/lapp/web/v1/web_pb.ts b/frontend/src/gen/lapp/web/v1/web_pb.ts index 20e1c15..8da236f 100644 --- a/frontend/src/gen/lapp/web/v1/web_pb.ts +++ b/frontend/src/gen/lapp/web/v1/web_pb.ts @@ -1,4 +1,4 @@ -// @generated by protoc-gen-es v2.12.0 with parameter "target=ts" +// @generated by protoc-gen-es v2.12.1 with parameter "target=ts" // @generated from file lapp/web/v1/web.proto (package lapp.web.v1, syntax proto3) /* eslint-disable */ @@ -12,7 +12,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file lapp/web/v1/web.proto. */ export const file_lapp_web_v1_web: GenFile = /*@__PURE__*/ - fileDesc("ChVsYXBwL3dlYi92MS93ZWIucHJvdG8SC2xhcHAud2ViLnYxIokCCglXb3Jrc3BhY2USDAoEbmFtZRgBIAEoCRIUCgx3b3Jrc3BhY2VfaWQYAiABKAkSLAoGc3RhdHVzGAMgASgOMhwubGFwcC53ZWIudjEuV29ya3NwYWNlU3RhdHVzEhYKDmxvZ19maWxlX2NvdW50GAQgASgFEhUKDXBhdHRlcm5fY291bnQYBSABKAUSNwoUbGF0ZXN0X2Rpc2NvdmVyeV9ydW4YBiABKAsyGS5sYXBwLndlYi52MS5EaXNjb3ZlcnlSdW4SQgofbGF0ZXN0X3N1Y2Nlc3NmdWxfZGlzY292ZXJ5X3J1bhgHIAEoCzIZLmxhcHAud2ViLnYxLkRpc2NvdmVyeVJ1biJuCgdMb2dGaWxlEgwKBG5hbWUYASABKAkSEQoJZmlsZV9uYW1lGAIgASgJEhIKCnNpemVfYnl0ZXMYAyABKAMSLgoKdXBkYXRlZF9hdBgEIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXAi3QMKDERpc2NvdmVyeVJ1bhIMCgRuYW1lGAEgASgJEhgKEGRpc2NvdmVyeV9ydW5faWQYAiABKAkSLQoFc3RhdGUYAyABKA4yHi5sYXBwLndlYi52MS5EaXNjb3ZlcnlSdW5TdGF0ZRIwCgxjdXJyZW50X3N0ZXAYBCABKA4yGi5sYXBwLndlYi52MS5EaXNjb3ZlcnlTdGVwEhwKEHByb2dyZXNzX21lc3NhZ2UYBSABKAlCAhgBEhkKDWVycm9yX21lc3NhZ2UYBiABKAlCAhgBEi4KCnN0YXJ0ZWRfYXQYByABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi8KC2ZpbmlzaGVkX2F0GAggASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIWCg5sb2dfZmlsZV9jb3VudBgJIAEoBRIVCg1wYXR0ZXJuX2NvdW50GAogASgFEhcKD3VubWF0Y2hlZF9jb3VudBgLIAEoBRIzCghwcm9ncmVzcxgMIAEoCzIhLmxhcHAud2ViLnYxLkRpc2NvdmVyeVJ1blByb2dyZXNzEi0KBWVycm9yGA0gASgLMh4ubGFwcC53ZWIudjEuRGlzY292ZXJ5UnVuRXJyb3IifwoURGlzY292ZXJ5UnVuUHJvZ3Jlc3MSKAoEc3RlcBgBIAEoDjIaLmxhcHAud2ViLnYxLkRpc2NvdmVyeVN0ZXASPQoLbGFiZWxfYmF0Y2gYAiABKAsyKC5sYXBwLndlYi52MS5EaXNjb3ZlcnlMYWJlbEJhdGNoUHJvZ3Jlc3MiqwEKG0Rpc2NvdmVyeUxhYmVsQmF0Y2hQcm9ncmVzcxINCgVldmVudBgBIAEoCRIUCgxiYXRjaF9udW1iZXIYAiABKAUSEwoLYmF0Y2hfY291bnQYAyABKAUSEgoKYmF0Y2hfc2l6ZRgEIAEoBRIPCgdhdHRlbXB0GAUgASgFEhQKDG1heF9hdHRlbXB0cxgGIAEoBRIXCg9jb21wbGV0ZWRfY291bnQYByABKAUiXAoRRGlzY292ZXJ5UnVuRXJyb3ISDAoEY29kZRgBIAEoCRIPCgdtZXNzYWdlGAIgASgJEigKBHN0ZXAYAyABKA4yGi5sYXBwLndlYi52MS5EaXNjb3ZlcnlTdGVwIoMCCgdQYXR0ZXJuEgwKBG5hbWUYASABKAkSEgoKcGF0dGVybl9pZBgCIAEoCRITCgtzZW1hbnRpY19pZBgDIAEoCRITCgtkZXNjcmlwdGlvbhgEIAEoCRIQCgh0ZW1wbGF0ZRgFIAEoCRINCgVjb3VudBgGIAEoBRIoCgpmaXJzdF9zZWVuGAcgASgLMhQubGFwcC53ZWIudjEuTGluZVJlZhInCglsYXN0X3NlZW4YCCABKAsyFC5sYXBwLndlYi52MS5MaW5lUmVmEicKCWxpbmVfcmVmcxgJIAMoCzIULmxhcHAud2ViLnYxLkxpbmVSZWYSDwoHc2FtcGxlcxgKIAMoCSIxCgdMaW5lUmVmEhEKCWZpbGVfbmFtZRgBIAEoCRITCgtsaW5lX251bWJlchgCIAEoBSI1CgxFcnJvclBhdHRlcm4SJQoHcGF0dGVybhgBIAEoCzIULmxhcHAud2ViLnYxLlBhdHRlcm4iTQoSVW5tYXRjaGVkRXJyb3JMaW5lEhEKCWZpbGVfbmFtZRgBIAEoCRITCgtsaW5lX251bWJlchgCIAEoBRIPCgdjb250ZW50GAMgASgJIhcKFUxpc3RXb3Jrc3BhY2VzUmVxdWVzdCJEChZMaXN0V29ya3NwYWNlc1Jlc3BvbnNlEioKCndvcmtzcGFjZXMYASADKAsyFi5sYXBwLndlYi52MS5Xb3Jrc3BhY2UiIwoTR2V0V29ya3NwYWNlUmVxdWVzdBIMCgRuYW1lGAEgASgJIkEKFEdldFdvcmtzcGFjZVJlc3BvbnNlEikKCXdvcmtzcGFjZRgBIAEoCzIWLmxhcHAud2ViLnYxLldvcmtzcGFjZSIuChZDcmVhdGVXb3Jrc3BhY2VSZXF1ZXN0EhQKDHdvcmtzcGFjZV9pZBgBIAEoCSJEChdDcmVhdGVXb3Jrc3BhY2VSZXNwb25zZRIpCgl3b3Jrc3BhY2UYASABKAsyFi5sYXBwLndlYi52MS5Xb3Jrc3BhY2UiJgoWRGVsZXRlV29ya3NwYWNlUmVxdWVzdBIMCgRuYW1lGAEgASgJIhkKF0RlbGV0ZVdvcmtzcGFjZVJlc3BvbnNlIiUKE0xpc3RMb2dGaWxlc1JlcXVlc3QSDgoGcGFyZW50GAEgASgJIj8KFExpc3RMb2dGaWxlc1Jlc3BvbnNlEicKCWxvZ19maWxlcxgBIAMoCzIULmxhcHAud2ViLnYxLkxvZ0ZpbGUiSgoUVXBsb2FkTG9nRmlsZVJlcXVlc3QSDgoGcGFyZW50GAEgASgJEhEKCWZpbGVfbmFtZRgCIAEoCRIPCgdjb250ZW50GAMgASgMIj8KFVVwbG9hZExvZ0ZpbGVSZXNwb25zZRImCghsb2dfZmlsZRgBIAEoCzIULmxhcHAud2ViLnYxLkxvZ0ZpbGUiJAoURGVsZXRlTG9nRmlsZVJlcXVlc3QSDAoEbmFtZRgBIAEoCSIXChVEZWxldGVMb2dGaWxlUmVzcG9uc2UiOgoZQ3JlYXRlRGlzY292ZXJ5UnVuUmVxdWVzdBIOCgZwYXJlbnQYASABKAkSDQoFbW9kZWwYAiABKAkiTgoaQ3JlYXRlRGlzY292ZXJ5UnVuUmVzcG9uc2USMAoNZGlzY292ZXJ5X3J1bhgBIAEoCzIZLmxhcHAud2ViLnYxLkRpc2NvdmVyeVJ1biImChZHZXREaXNjb3ZlcnlSdW5SZXF1ZXN0EgwKBG5hbWUYASABKAkiSwoXR2V0RGlzY292ZXJ5UnVuUmVzcG9uc2USMAoNZGlzY292ZXJ5X3J1bhgBIAEoCzIZLmxhcHAud2ViLnYxLkRpc2NvdmVyeVJ1biIqChhMaXN0RGlzY292ZXJ5UnVuc1JlcXVlc3QSDgoGcGFyZW50GAEgASgJIk4KGUxpc3REaXNjb3ZlcnlSdW5zUmVzcG9uc2USMQoOZGlzY292ZXJ5X3J1bnMYASADKAsyGS5sYXBwLndlYi52MS5EaXNjb3ZlcnlSdW4iJQoTTGlzdFBhdHRlcm5zUmVxdWVzdBIOCgZwYXJlbnQYASABKAkiPgoUTGlzdFBhdHRlcm5zUmVzcG9uc2USJgoIcGF0dGVybnMYASADKAsyFC5sYXBwLndlYi52MS5QYXR0ZXJuIiEKEUdldFBhdHRlcm5SZXF1ZXN0EgwKBG5hbWUYASABKAkiOwoSR2V0UGF0dGVyblJlc3BvbnNlEiUKB3BhdHRlcm4YASABKAsyFC5sYXBwLndlYi52MS5QYXR0ZXJuIiYKFEdldEVycm9yc1ZpZXdSZXF1ZXN0Eg4KBnBhcmVudBgBIAEoCSKKAQoVR2V0RXJyb3JzVmlld1Jlc3BvbnNlEjEKDmVycm9yX3BhdHRlcm5zGAEgAygLMhkubGFwcC53ZWIudjEuRXJyb3JQYXR0ZXJuEj4KFXVubWF0Y2hlZF9lcnJvcl9saW5lcxgCIAMoCzIfLmxhcHAud2ViLnYxLlVubWF0Y2hlZEVycm9yTGluZSqqAQoPV29ya3NwYWNlU3RhdHVzEiAKHFdPUktTUEFDRV9TVEFUVVNfVU5TUEVDSUZJRUQQABIaChZXT1JLU1BBQ0VfU1RBVFVTX0VNUFRZEAESIAocV09SS1NQQUNFX1NUQVRVU19ESVNDT1ZFUklORxACEhoKFldPUktTUEFDRV9TVEFUVVNfUkVBRFkQAxIbChdXT1JLU1BBQ0VfU1RBVFVTX0ZBSUxFRBAEKrwBChFEaXNjb3ZlcnlSdW5TdGF0ZRIjCh9ESVNDT1ZFUllfUlVOX1NUQVRFX1VOU1BFQ0lGSUVEEAASHgoaRElTQ09WRVJZX1JVTl9TVEFURV9RVUVVRUQQARIfChtESVNDT1ZFUllfUlVOX1NUQVRFX1JVTk5JTkcQAhIhCh1ESVNDT1ZFUllfUlVOX1NUQVRFX1NVQ0NFRURFRBADEh4KGkRJU0NPVkVSWV9SVU5fU1RBVEVfRkFJTEVEEAQq5wEKDURpc2NvdmVyeVN0ZXASHgoaRElTQ09WRVJZX1NURVBfVU5TUEVDSUZJRUQQABIfChtESVNDT1ZFUllfU1RFUF9SRUFESU5HX0xPR1MQARIiCh5ESVNDT1ZFUllfU1RFUF9NRVJHSU5HX0VOVFJJRVMQAhInCiNESVNDT1ZFUllfU1RFUF9ESVNDT1ZFUklOR19QQVRURVJOUxADEiQKIERJU0NPVkVSWV9TVEVQX0xBQkVMSU5HX1BBVFRFUk5TEAQSIgoeRElTQ09WRVJZX1NURVBfV1JJVElOR19SRVNVTFRTEAUyqAkKEFdvcmtzcGFjZVNlcnZpY2USWQoOTGlzdFdvcmtzcGFjZXMSIi5sYXBwLndlYi52MS5MaXN0V29ya3NwYWNlc1JlcXVlc3QaIy5sYXBwLndlYi52MS5MaXN0V29ya3NwYWNlc1Jlc3BvbnNlElMKDEdldFdvcmtzcGFjZRIgLmxhcHAud2ViLnYxLkdldFdvcmtzcGFjZVJlcXVlc3QaIS5sYXBwLndlYi52MS5HZXRXb3Jrc3BhY2VSZXNwb25zZRJcCg9DcmVhdGVXb3Jrc3BhY2USIy5sYXBwLndlYi52MS5DcmVhdGVXb3Jrc3BhY2VSZXF1ZXN0GiQubGFwcC53ZWIudjEuQ3JlYXRlV29ya3NwYWNlUmVzcG9uc2USXAoPRGVsZXRlV29ya3NwYWNlEiMubGFwcC53ZWIudjEuRGVsZXRlV29ya3NwYWNlUmVxdWVzdBokLmxhcHAud2ViLnYxLkRlbGV0ZVdvcmtzcGFjZVJlc3BvbnNlElMKDExpc3RMb2dGaWxlcxIgLmxhcHAud2ViLnYxLkxpc3RMb2dGaWxlc1JlcXVlc3QaIS5sYXBwLndlYi52MS5MaXN0TG9nRmlsZXNSZXNwb25zZRJWCg1VcGxvYWRMb2dGaWxlEiEubGFwcC53ZWIudjEuVXBsb2FkTG9nRmlsZVJlcXVlc3QaIi5sYXBwLndlYi52MS5VcGxvYWRMb2dGaWxlUmVzcG9uc2USVgoNRGVsZXRlTG9nRmlsZRIhLmxhcHAud2ViLnYxLkRlbGV0ZUxvZ0ZpbGVSZXF1ZXN0GiIubGFwcC53ZWIudjEuRGVsZXRlTG9nRmlsZVJlc3BvbnNlEmUKEkNyZWF0ZURpc2NvdmVyeVJ1bhImLmxhcHAud2ViLnYxLkNyZWF0ZURpc2NvdmVyeVJ1blJlcXVlc3QaJy5sYXBwLndlYi52MS5DcmVhdGVEaXNjb3ZlcnlSdW5SZXNwb25zZRJcCg9HZXREaXNjb3ZlcnlSdW4SIy5sYXBwLndlYi52MS5HZXREaXNjb3ZlcnlSdW5SZXF1ZXN0GiQubGFwcC53ZWIudjEuR2V0RGlzY292ZXJ5UnVuUmVzcG9uc2USYgoRTGlzdERpc2NvdmVyeVJ1bnMSJS5sYXBwLndlYi52MS5MaXN0RGlzY292ZXJ5UnVuc1JlcXVlc3QaJi5sYXBwLndlYi52MS5MaXN0RGlzY292ZXJ5UnVuc1Jlc3BvbnNlElMKDExpc3RQYXR0ZXJucxIgLmxhcHAud2ViLnYxLkxpc3RQYXR0ZXJuc1JlcXVlc3QaIS5sYXBwLndlYi52MS5MaXN0UGF0dGVybnNSZXNwb25zZRJNCgpHZXRQYXR0ZXJuEh4ubGFwcC53ZWIudjEuR2V0UGF0dGVyblJlcXVlc3QaHy5sYXBwLndlYi52MS5HZXRQYXR0ZXJuUmVzcG9uc2USVgoNR2V0RXJyb3JzVmlldxIhLmxhcHAud2ViLnYxLkdldEVycm9yc1ZpZXdSZXF1ZXN0GiIubGFwcC53ZWIudjEuR2V0RXJyb3JzVmlld1Jlc3BvbnNlQjBaLmdpdGh1Yi5jb20vc3RycmwvbGFwcC9nZW4vZ28vbGFwcC93ZWIvdjE7d2VidjFiBnByb3RvMw", [file_google_protobuf_timestamp]); + fileDesc("ChVsYXBwL3dlYi92MS93ZWIucHJvdG8SC2xhcHAud2ViLnYxIokCCglXb3Jrc3BhY2USDAoEbmFtZRgBIAEoCRIUCgx3b3Jrc3BhY2VfaWQYAiABKAkSLAoGc3RhdHVzGAMgASgOMhwubGFwcC53ZWIudjEuV29ya3NwYWNlU3RhdHVzEhYKDmxvZ19maWxlX2NvdW50GAQgASgFEhUKDXBhdHRlcm5fY291bnQYBSABKAUSNwoUbGF0ZXN0X2Rpc2NvdmVyeV9ydW4YBiABKAsyGS5sYXBwLndlYi52MS5EaXNjb3ZlcnlSdW4SQgofbGF0ZXN0X3N1Y2Nlc3NmdWxfZGlzY292ZXJ5X3J1bhgHIAEoCzIZLmxhcHAud2ViLnYxLkRpc2NvdmVyeVJ1biJuCgdMb2dGaWxlEgwKBG5hbWUYASABKAkSEQoJZmlsZV9uYW1lGAIgASgJEhIKCnNpemVfYnl0ZXMYAyABKAMSLgoKdXBkYXRlZF9hdBgEIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXAi3QMKDERpc2NvdmVyeVJ1bhIMCgRuYW1lGAEgASgJEhgKEGRpc2NvdmVyeV9ydW5faWQYAiABKAkSLQoFc3RhdGUYAyABKA4yHi5sYXBwLndlYi52MS5EaXNjb3ZlcnlSdW5TdGF0ZRIwCgxjdXJyZW50X3N0ZXAYBCABKA4yGi5sYXBwLndlYi52MS5EaXNjb3ZlcnlTdGVwEhwKEHByb2dyZXNzX21lc3NhZ2UYBSABKAlCAhgBEhkKDWVycm9yX21lc3NhZ2UYBiABKAlCAhgBEi4KCnN0YXJ0ZWRfYXQYByABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi8KC2ZpbmlzaGVkX2F0GAggASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIWCg5sb2dfZmlsZV9jb3VudBgJIAEoBRIVCg1wYXR0ZXJuX2NvdW50GAogASgFEhcKD3VubWF0Y2hlZF9jb3VudBgLIAEoBRIzCghwcm9ncmVzcxgMIAEoCzIhLmxhcHAud2ViLnYxLkRpc2NvdmVyeVJ1blByb2dyZXNzEi0KBWVycm9yGA0gASgLMh4ubGFwcC53ZWIudjEuRGlzY292ZXJ5UnVuRXJyb3IifwoURGlzY292ZXJ5UnVuUHJvZ3Jlc3MSKAoEc3RlcBgBIAEoDjIaLmxhcHAud2ViLnYxLkRpc2NvdmVyeVN0ZXASPQoLbGFiZWxfYmF0Y2gYAiABKAsyKC5sYXBwLndlYi52MS5EaXNjb3ZlcnlMYWJlbEJhdGNoUHJvZ3Jlc3MiqwEKG0Rpc2NvdmVyeUxhYmVsQmF0Y2hQcm9ncmVzcxINCgVldmVudBgBIAEoCRIUCgxiYXRjaF9udW1iZXIYAiABKAUSEwoLYmF0Y2hfY291bnQYAyABKAUSEgoKYmF0Y2hfc2l6ZRgEIAEoBRIPCgdhdHRlbXB0GAUgASgFEhQKDG1heF9hdHRlbXB0cxgGIAEoBRIXCg9jb21wbGV0ZWRfY291bnQYByABKAUiXAoRRGlzY292ZXJ5UnVuRXJyb3ISDAoEY29kZRgBIAEoCRIPCgdtZXNzYWdlGAIgASgJEigKBHN0ZXAYAyABKA4yGi5sYXBwLndlYi52MS5EaXNjb3ZlcnlTdGVwItcCCglJbXBvcnRSdW4SDAoEbmFtZRgBIAEoCRIVCg1pbXBvcnRfcnVuX2lkGAIgASgJEioKBXN0YXRlGAMgASgOMhsubGFwcC53ZWIudjEuSW1wb3J0UnVuU3RhdGUSDgoGc291cmNlGAQgASgJEi4KCnN0YXJ0ZWRfYXQYBSABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEi8KC2ZpbmlzaGVkX2F0GAYgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBITCgtlbnRyeV9jb3VudBgHIAEoBRIVCg1sb2dfZmlsZV9uYW1lGAggASgJEjAKCHByb2dyZXNzGAkgASgLMh4ubGFwcC53ZWIudjEuSW1wb3J0UnVuUHJvZ3Jlc3MSKgoFZXJyb3IYCiABKAsyGy5sYXBwLndlYi52MS5JbXBvcnRSdW5FcnJvciIqChFJbXBvcnRSdW5Qcm9ncmVzcxIVCg1mZXRjaGVkX2NvdW50GAEgASgFIi8KDkltcG9ydFJ1bkVycm9yEgwKBGNvZGUYASABKAkSDwoHbWVzc2FnZRgCIAEoCSKjAQoQR2NwTG9nZ2luZ1NvdXJjZRISCgpwcm9qZWN0X2lkGAEgASgJEg4KBmZpbHRlchgCIAEoCRIuCgpzdGFydF90aW1lGAMgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcBIsCghlbmRfdGltZRgEIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASDQoFbGltaXQYBSABKAUigwIKB1BhdHRlcm4SDAoEbmFtZRgBIAEoCRISCgpwYXR0ZXJuX2lkGAIgASgJEhMKC3NlbWFudGljX2lkGAMgASgJEhMKC2Rlc2NyaXB0aW9uGAQgASgJEhAKCHRlbXBsYXRlGAUgASgJEg0KBWNvdW50GAYgASgFEigKCmZpcnN0X3NlZW4YByABKAsyFC5sYXBwLndlYi52MS5MaW5lUmVmEicKCWxhc3Rfc2VlbhgIIAEoCzIULmxhcHAud2ViLnYxLkxpbmVSZWYSJwoJbGluZV9yZWZzGAkgAygLMhQubGFwcC53ZWIudjEuTGluZVJlZhIPCgdzYW1wbGVzGAogAygJIjEKB0xpbmVSZWYSEQoJZmlsZV9uYW1lGAEgASgJEhMKC2xpbmVfbnVtYmVyGAIgASgFIjUKDEVycm9yUGF0dGVybhIlCgdwYXR0ZXJuGAEgASgLMhQubGFwcC53ZWIudjEuUGF0dGVybiJNChJVbm1hdGNoZWRFcnJvckxpbmUSEQoJZmlsZV9uYW1lGAEgASgJEhMKC2xpbmVfbnVtYmVyGAIgASgFEg8KB2NvbnRlbnQYAyABKAkiFwoVTGlzdFdvcmtzcGFjZXNSZXF1ZXN0IkQKFkxpc3RXb3Jrc3BhY2VzUmVzcG9uc2USKgoKd29ya3NwYWNlcxgBIAMoCzIWLmxhcHAud2ViLnYxLldvcmtzcGFjZSIjChNHZXRXb3Jrc3BhY2VSZXF1ZXN0EgwKBG5hbWUYASABKAkiQQoUR2V0V29ya3NwYWNlUmVzcG9uc2USKQoJd29ya3NwYWNlGAEgASgLMhYubGFwcC53ZWIudjEuV29ya3NwYWNlIi4KFkNyZWF0ZVdvcmtzcGFjZVJlcXVlc3QSFAoMd29ya3NwYWNlX2lkGAEgASgJIkQKF0NyZWF0ZVdvcmtzcGFjZVJlc3BvbnNlEikKCXdvcmtzcGFjZRgBIAEoCzIWLmxhcHAud2ViLnYxLldvcmtzcGFjZSImChZEZWxldGVXb3Jrc3BhY2VSZXF1ZXN0EgwKBG5hbWUYASABKAkiGQoXRGVsZXRlV29ya3NwYWNlUmVzcG9uc2UiJQoTTGlzdExvZ0ZpbGVzUmVxdWVzdBIOCgZwYXJlbnQYASABKAkiPwoUTGlzdExvZ0ZpbGVzUmVzcG9uc2USJwoJbG9nX2ZpbGVzGAEgAygLMhQubGFwcC53ZWIudjEuTG9nRmlsZSJKChRVcGxvYWRMb2dGaWxlUmVxdWVzdBIOCgZwYXJlbnQYASABKAkSEQoJZmlsZV9uYW1lGAIgASgJEg8KB2NvbnRlbnQYAyABKAwiPwoVVXBsb2FkTG9nRmlsZVJlc3BvbnNlEiYKCGxvZ19maWxlGAEgASgLMhQubGFwcC53ZWIudjEuTG9nRmlsZSIkChREZWxldGVMb2dGaWxlUmVxdWVzdBIMCgRuYW1lGAEgASgJIhcKFURlbGV0ZUxvZ0ZpbGVSZXNwb25zZSI6ChlDcmVhdGVEaXNjb3ZlcnlSdW5SZXF1ZXN0Eg4KBnBhcmVudBgBIAEoCRINCgVtb2RlbBgCIAEoCSJOChpDcmVhdGVEaXNjb3ZlcnlSdW5SZXNwb25zZRIwCg1kaXNjb3ZlcnlfcnVuGAEgASgLMhkubGFwcC53ZWIudjEuRGlzY292ZXJ5UnVuIiYKFkdldERpc2NvdmVyeVJ1blJlcXVlc3QSDAoEbmFtZRgBIAEoCSJLChdHZXREaXNjb3ZlcnlSdW5SZXNwb25zZRIwCg1kaXNjb3ZlcnlfcnVuGAEgASgLMhkubGFwcC53ZWIudjEuRGlzY292ZXJ5UnVuIioKGExpc3REaXNjb3ZlcnlSdW5zUmVxdWVzdBIOCgZwYXJlbnQYASABKAkiTgoZTGlzdERpc2NvdmVyeVJ1bnNSZXNwb25zZRIxCg5kaXNjb3ZlcnlfcnVucxgBIAMoCzIZLmxhcHAud2ViLnYxLkRpc2NvdmVyeVJ1biJjChFJbXBvcnRMb2dzUmVxdWVzdBIOCgZwYXJlbnQYASABKAkSNAoLZ2NwX2xvZ2dpbmcYAiABKAsyHS5sYXBwLndlYi52MS5HY3BMb2dnaW5nU291cmNlSABCCAoGc291cmNlIkAKEkltcG9ydExvZ3NSZXNwb25zZRIqCgppbXBvcnRfcnVuGAEgASgLMhYubGFwcC53ZWIudjEuSW1wb3J0UnVuIiMKE0dldEltcG9ydFJ1blJlcXVlc3QSDAoEbmFtZRgBIAEoCSJCChRHZXRJbXBvcnRSdW5SZXNwb25zZRIqCgppbXBvcnRfcnVuGAEgASgLMhYubGFwcC53ZWIudjEuSW1wb3J0UnVuIicKFUxpc3RJbXBvcnRSdW5zUmVxdWVzdBIOCgZwYXJlbnQYASABKAkiRQoWTGlzdEltcG9ydFJ1bnNSZXNwb25zZRIrCgtpbXBvcnRfcnVucxgBIAMoCzIWLmxhcHAud2ViLnYxLkltcG9ydFJ1biIlChNMaXN0UGF0dGVybnNSZXF1ZXN0Eg4KBnBhcmVudBgBIAEoCSI+ChRMaXN0UGF0dGVybnNSZXNwb25zZRImCghwYXR0ZXJucxgBIAMoCzIULmxhcHAud2ViLnYxLlBhdHRlcm4iIQoRR2V0UGF0dGVyblJlcXVlc3QSDAoEbmFtZRgBIAEoCSI7ChJHZXRQYXR0ZXJuUmVzcG9uc2USJQoHcGF0dGVybhgBIAEoCzIULmxhcHAud2ViLnYxLlBhdHRlcm4iJgoUR2V0RXJyb3JzVmlld1JlcXVlc3QSDgoGcGFyZW50GAEgASgJIooBChVHZXRFcnJvcnNWaWV3UmVzcG9uc2USMQoOZXJyb3JfcGF0dGVybnMYASADKAsyGS5sYXBwLndlYi52MS5FcnJvclBhdHRlcm4SPgoVdW5tYXRjaGVkX2Vycm9yX2xpbmVzGAIgAygLMh8ubGFwcC53ZWIudjEuVW5tYXRjaGVkRXJyb3JMaW5lKqoBCg9Xb3Jrc3BhY2VTdGF0dXMSIAocV09SS1NQQUNFX1NUQVRVU19VTlNQRUNJRklFRBAAEhoKFldPUktTUEFDRV9TVEFUVVNfRU1QVFkQARIgChxXT1JLU1BBQ0VfU1RBVFVTX0RJU0NPVkVSSU5HEAISGgoWV09SS1NQQUNFX1NUQVRVU19SRUFEWRADEhsKF1dPUktTUEFDRV9TVEFUVVNfRkFJTEVEEAQqvAEKEURpc2NvdmVyeVJ1blN0YXRlEiMKH0RJU0NPVkVSWV9SVU5fU1RBVEVfVU5TUEVDSUZJRUQQABIeChpESVNDT1ZFUllfUlVOX1NUQVRFX1FVRVVFRBABEh8KG0RJU0NPVkVSWV9SVU5fU1RBVEVfUlVOTklORxACEiEKHURJU0NPVkVSWV9SVU5fU1RBVEVfU1VDQ0VFREVEEAMSHgoaRElTQ09WRVJZX1JVTl9TVEFURV9GQUlMRUQQBCrnAQoNRGlzY292ZXJ5U3RlcBIeChpESVNDT1ZFUllfU1RFUF9VTlNQRUNJRklFRBAAEh8KG0RJU0NPVkVSWV9TVEVQX1JFQURJTkdfTE9HUxABEiIKHkRJU0NPVkVSWV9TVEVQX01FUkdJTkdfRU5UUklFUxACEicKI0RJU0NPVkVSWV9TVEVQX0RJU0NPVkVSSU5HX1BBVFRFUk5TEAMSJAogRElTQ09WRVJZX1NURVBfTEFCRUxJTkdfUEFUVEVSTlMQBBIiCh5ESVNDT1ZFUllfU1RFUF9XUklUSU5HX1JFU1VMVFMQBSqqAQoOSW1wb3J0UnVuU3RhdGUSIAocSU1QT1JUX1JVTl9TVEFURV9VTlNQRUNJRklFRBAAEhsKF0lNUE9SVF9SVU5fU1RBVEVfUVVFVUVEEAESHAoYSU1QT1JUX1JVTl9TVEFURV9SVU5OSU5HEAISHgoaSU1QT1JUX1JVTl9TVEFURV9TVUNDRUVERUQQAxIbChdJTVBPUlRfUlVOX1NUQVRFX0ZBSUxFRBAEMqcLChBXb3Jrc3BhY2VTZXJ2aWNlElkKDkxpc3RXb3Jrc3BhY2VzEiIubGFwcC53ZWIudjEuTGlzdFdvcmtzcGFjZXNSZXF1ZXN0GiMubGFwcC53ZWIudjEuTGlzdFdvcmtzcGFjZXNSZXNwb25zZRJTCgxHZXRXb3Jrc3BhY2USIC5sYXBwLndlYi52MS5HZXRXb3Jrc3BhY2VSZXF1ZXN0GiEubGFwcC53ZWIudjEuR2V0V29ya3NwYWNlUmVzcG9uc2USXAoPQ3JlYXRlV29ya3NwYWNlEiMubGFwcC53ZWIudjEuQ3JlYXRlV29ya3NwYWNlUmVxdWVzdBokLmxhcHAud2ViLnYxLkNyZWF0ZVdvcmtzcGFjZVJlc3BvbnNlElwKD0RlbGV0ZVdvcmtzcGFjZRIjLmxhcHAud2ViLnYxLkRlbGV0ZVdvcmtzcGFjZVJlcXVlc3QaJC5sYXBwLndlYi52MS5EZWxldGVXb3Jrc3BhY2VSZXNwb25zZRJTCgxMaXN0TG9nRmlsZXMSIC5sYXBwLndlYi52MS5MaXN0TG9nRmlsZXNSZXF1ZXN0GiEubGFwcC53ZWIudjEuTGlzdExvZ0ZpbGVzUmVzcG9uc2USVgoNVXBsb2FkTG9nRmlsZRIhLmxhcHAud2ViLnYxLlVwbG9hZExvZ0ZpbGVSZXF1ZXN0GiIubGFwcC53ZWIudjEuVXBsb2FkTG9nRmlsZVJlc3BvbnNlElYKDURlbGV0ZUxvZ0ZpbGUSIS5sYXBwLndlYi52MS5EZWxldGVMb2dGaWxlUmVxdWVzdBoiLmxhcHAud2ViLnYxLkRlbGV0ZUxvZ0ZpbGVSZXNwb25zZRJlChJDcmVhdGVEaXNjb3ZlcnlSdW4SJi5sYXBwLndlYi52MS5DcmVhdGVEaXNjb3ZlcnlSdW5SZXF1ZXN0GicubGFwcC53ZWIudjEuQ3JlYXRlRGlzY292ZXJ5UnVuUmVzcG9uc2USXAoPR2V0RGlzY292ZXJ5UnVuEiMubGFwcC53ZWIudjEuR2V0RGlzY292ZXJ5UnVuUmVxdWVzdBokLmxhcHAud2ViLnYxLkdldERpc2NvdmVyeVJ1blJlc3BvbnNlEmIKEUxpc3REaXNjb3ZlcnlSdW5zEiUubGFwcC53ZWIudjEuTGlzdERpc2NvdmVyeVJ1bnNSZXF1ZXN0GiYubGFwcC53ZWIudjEuTGlzdERpc2NvdmVyeVJ1bnNSZXNwb25zZRJNCgpJbXBvcnRMb2dzEh4ubGFwcC53ZWIudjEuSW1wb3J0TG9nc1JlcXVlc3QaHy5sYXBwLndlYi52MS5JbXBvcnRMb2dzUmVzcG9uc2USUwoMR2V0SW1wb3J0UnVuEiAubGFwcC53ZWIudjEuR2V0SW1wb3J0UnVuUmVxdWVzdBohLmxhcHAud2ViLnYxLkdldEltcG9ydFJ1blJlc3BvbnNlElkKDkxpc3RJbXBvcnRSdW5zEiIubGFwcC53ZWIudjEuTGlzdEltcG9ydFJ1bnNSZXF1ZXN0GiMubGFwcC53ZWIudjEuTGlzdEltcG9ydFJ1bnNSZXNwb25zZRJTCgxMaXN0UGF0dGVybnMSIC5sYXBwLndlYi52MS5MaXN0UGF0dGVybnNSZXF1ZXN0GiEubGFwcC53ZWIudjEuTGlzdFBhdHRlcm5zUmVzcG9uc2USTQoKR2V0UGF0dGVybhIeLmxhcHAud2ViLnYxLkdldFBhdHRlcm5SZXF1ZXN0Gh8ubGFwcC53ZWIudjEuR2V0UGF0dGVyblJlc3BvbnNlElYKDUdldEVycm9yc1ZpZXcSIS5sYXBwLndlYi52MS5HZXRFcnJvcnNWaWV3UmVxdWVzdBoiLmxhcHAud2ViLnYxLkdldEVycm9yc1ZpZXdSZXNwb25zZUIwWi5naXRodWIuY29tL3N0cnJsL2xhcHAvZ2VuL2dvL2xhcHAvd2ViL3YxO3dlYnYxYgZwcm90bzM", [file_google_protobuf_timestamp]); /** * @generated from message lapp.web.v1.Workspace @@ -268,6 +268,144 @@ export type DiscoveryRunError = Message<"lapp.web.v1.DiscoveryRunError"> & { export const DiscoveryRunErrorSchema: GenMessage = /*@__PURE__*/ messageDesc(file_lapp_web_v1_web, 5); +/** + * @generated from message lapp.web.v1.ImportRun + */ +export type ImportRun = Message<"lapp.web.v1.ImportRun"> & { + /** + * @generated from field: string name = 1; + */ + name: string; + + /** + * @generated from field: string import_run_id = 2; + */ + importRunId: string; + + /** + * @generated from field: lapp.web.v1.ImportRunState state = 3; + */ + state: ImportRunState; + + /** + * @generated from field: string source = 4; + */ + source: string; + + /** + * @generated from field: google.protobuf.Timestamp started_at = 5; + */ + startedAt?: Timestamp | undefined; + + /** + * @generated from field: google.protobuf.Timestamp finished_at = 6; + */ + finishedAt?: Timestamp | undefined; + + /** + * @generated from field: int32 entry_count = 7; + */ + entryCount: number; + + /** + * @generated from field: string log_file_name = 8; + */ + logFileName: string; + + /** + * @generated from field: lapp.web.v1.ImportRunProgress progress = 9; + */ + progress?: ImportRunProgress | undefined; + + /** + * @generated from field: lapp.web.v1.ImportRunError error = 10; + */ + error?: ImportRunError | undefined; +}; + +/** + * Describes the message lapp.web.v1.ImportRun. + * Use `create(ImportRunSchema)` to create a new message. + */ +export const ImportRunSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_lapp_web_v1_web, 6); + +/** + * @generated from message lapp.web.v1.ImportRunProgress + */ +export type ImportRunProgress = Message<"lapp.web.v1.ImportRunProgress"> & { + /** + * @generated from field: int32 fetched_count = 1; + */ + fetchedCount: number; +}; + +/** + * Describes the message lapp.web.v1.ImportRunProgress. + * Use `create(ImportRunProgressSchema)` to create a new message. + */ +export const ImportRunProgressSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_lapp_web_v1_web, 7); + +/** + * @generated from message lapp.web.v1.ImportRunError + */ +export type ImportRunError = Message<"lapp.web.v1.ImportRunError"> & { + /** + * @generated from field: string code = 1; + */ + code: string; + + /** + * @generated from field: string message = 2; + */ + message: string; +}; + +/** + * Describes the message lapp.web.v1.ImportRunError. + * Use `create(ImportRunErrorSchema)` to create a new message. + */ +export const ImportRunErrorSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_lapp_web_v1_web, 8); + +/** + * @generated from message lapp.web.v1.GcpLoggingSource + */ +export type GcpLoggingSource = Message<"lapp.web.v1.GcpLoggingSource"> & { + /** + * @generated from field: string project_id = 1; + */ + projectId: string; + + /** + * @generated from field: string filter = 2; + */ + filter: string; + + /** + * @generated from field: google.protobuf.Timestamp start_time = 3; + */ + startTime?: Timestamp | undefined; + + /** + * @generated from field: google.protobuf.Timestamp end_time = 4; + */ + endTime?: Timestamp | undefined; + + /** + * @generated from field: int32 limit = 5; + */ + limit: number; +}; + +/** + * Describes the message lapp.web.v1.GcpLoggingSource. + * Use `create(GcpLoggingSourceSchema)` to create a new message. + */ +export const GcpLoggingSourceSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_lapp_web_v1_web, 9); + /** * @generated from message lapp.web.v1.Pattern */ @@ -328,7 +466,7 @@ export type Pattern = Message<"lapp.web.v1.Pattern"> & { * Use `create(PatternSchema)` to create a new message. */ export const PatternSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 6); + messageDesc(file_lapp_web_v1_web, 10); /** * @generated from message lapp.web.v1.LineRef @@ -350,7 +488,7 @@ export type LineRef = Message<"lapp.web.v1.LineRef"> & { * Use `create(LineRefSchema)` to create a new message. */ export const LineRefSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 7); + messageDesc(file_lapp_web_v1_web, 11); /** * @generated from message lapp.web.v1.ErrorPattern @@ -367,7 +505,7 @@ export type ErrorPattern = Message<"lapp.web.v1.ErrorPattern"> & { * Use `create(ErrorPatternSchema)` to create a new message. */ export const ErrorPatternSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 8); + messageDesc(file_lapp_web_v1_web, 12); /** * @generated from message lapp.web.v1.UnmatchedErrorLine @@ -394,7 +532,7 @@ export type UnmatchedErrorLine = Message<"lapp.web.v1.UnmatchedErrorLine"> & { * Use `create(UnmatchedErrorLineSchema)` to create a new message. */ export const UnmatchedErrorLineSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 9); + messageDesc(file_lapp_web_v1_web, 13); /** * @generated from message lapp.web.v1.ListWorkspacesRequest @@ -407,7 +545,7 @@ export type ListWorkspacesRequest = Message<"lapp.web.v1.ListWorkspacesRequest"> * Use `create(ListWorkspacesRequestSchema)` to create a new message. */ export const ListWorkspacesRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 10); + messageDesc(file_lapp_web_v1_web, 14); /** * @generated from message lapp.web.v1.ListWorkspacesResponse @@ -424,7 +562,7 @@ export type ListWorkspacesResponse = Message<"lapp.web.v1.ListWorkspacesResponse * Use `create(ListWorkspacesResponseSchema)` to create a new message. */ export const ListWorkspacesResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 11); + messageDesc(file_lapp_web_v1_web, 15); /** * @generated from message lapp.web.v1.GetWorkspaceRequest @@ -441,7 +579,7 @@ export type GetWorkspaceRequest = Message<"lapp.web.v1.GetWorkspaceRequest"> & { * Use `create(GetWorkspaceRequestSchema)` to create a new message. */ export const GetWorkspaceRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 12); + messageDesc(file_lapp_web_v1_web, 16); /** * @generated from message lapp.web.v1.GetWorkspaceResponse @@ -458,7 +596,7 @@ export type GetWorkspaceResponse = Message<"lapp.web.v1.GetWorkspaceResponse"> & * Use `create(GetWorkspaceResponseSchema)` to create a new message. */ export const GetWorkspaceResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 13); + messageDesc(file_lapp_web_v1_web, 17); /** * @generated from message lapp.web.v1.CreateWorkspaceRequest @@ -475,7 +613,7 @@ export type CreateWorkspaceRequest = Message<"lapp.web.v1.CreateWorkspaceRequest * Use `create(CreateWorkspaceRequestSchema)` to create a new message. */ export const CreateWorkspaceRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 14); + messageDesc(file_lapp_web_v1_web, 18); /** * @generated from message lapp.web.v1.CreateWorkspaceResponse @@ -492,7 +630,7 @@ export type CreateWorkspaceResponse = Message<"lapp.web.v1.CreateWorkspaceRespon * Use `create(CreateWorkspaceResponseSchema)` to create a new message. */ export const CreateWorkspaceResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 15); + messageDesc(file_lapp_web_v1_web, 19); /** * @generated from message lapp.web.v1.DeleteWorkspaceRequest @@ -509,7 +647,7 @@ export type DeleteWorkspaceRequest = Message<"lapp.web.v1.DeleteWorkspaceRequest * Use `create(DeleteWorkspaceRequestSchema)` to create a new message. */ export const DeleteWorkspaceRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 16); + messageDesc(file_lapp_web_v1_web, 20); /** * @generated from message lapp.web.v1.DeleteWorkspaceResponse @@ -522,7 +660,7 @@ export type DeleteWorkspaceResponse = Message<"lapp.web.v1.DeleteWorkspaceRespon * Use `create(DeleteWorkspaceResponseSchema)` to create a new message. */ export const DeleteWorkspaceResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 17); + messageDesc(file_lapp_web_v1_web, 21); /** * @generated from message lapp.web.v1.ListLogFilesRequest @@ -539,7 +677,7 @@ export type ListLogFilesRequest = Message<"lapp.web.v1.ListLogFilesRequest"> & { * Use `create(ListLogFilesRequestSchema)` to create a new message. */ export const ListLogFilesRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 18); + messageDesc(file_lapp_web_v1_web, 22); /** * @generated from message lapp.web.v1.ListLogFilesResponse @@ -556,7 +694,7 @@ export type ListLogFilesResponse = Message<"lapp.web.v1.ListLogFilesResponse"> & * Use `create(ListLogFilesResponseSchema)` to create a new message. */ export const ListLogFilesResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 19); + messageDesc(file_lapp_web_v1_web, 23); /** * @generated from message lapp.web.v1.UploadLogFileRequest @@ -583,7 +721,7 @@ export type UploadLogFileRequest = Message<"lapp.web.v1.UploadLogFileRequest"> & * Use `create(UploadLogFileRequestSchema)` to create a new message. */ export const UploadLogFileRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 20); + messageDesc(file_lapp_web_v1_web, 24); /** * @generated from message lapp.web.v1.UploadLogFileResponse @@ -600,7 +738,7 @@ export type UploadLogFileResponse = Message<"lapp.web.v1.UploadLogFileResponse"> * Use `create(UploadLogFileResponseSchema)` to create a new message. */ export const UploadLogFileResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 21); + messageDesc(file_lapp_web_v1_web, 25); /** * @generated from message lapp.web.v1.DeleteLogFileRequest @@ -617,7 +755,7 @@ export type DeleteLogFileRequest = Message<"lapp.web.v1.DeleteLogFileRequest"> & * Use `create(DeleteLogFileRequestSchema)` to create a new message. */ export const DeleteLogFileRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 22); + messageDesc(file_lapp_web_v1_web, 26); /** * @generated from message lapp.web.v1.DeleteLogFileResponse @@ -630,7 +768,7 @@ export type DeleteLogFileResponse = Message<"lapp.web.v1.DeleteLogFileResponse"> * Use `create(DeleteLogFileResponseSchema)` to create a new message. */ export const DeleteLogFileResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 23); + messageDesc(file_lapp_web_v1_web, 27); /** * @generated from message lapp.web.v1.CreateDiscoveryRunRequest @@ -652,7 +790,7 @@ export type CreateDiscoveryRunRequest = Message<"lapp.web.v1.CreateDiscoveryRunR * Use `create(CreateDiscoveryRunRequestSchema)` to create a new message. */ export const CreateDiscoveryRunRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 24); + messageDesc(file_lapp_web_v1_web, 28); /** * @generated from message lapp.web.v1.CreateDiscoveryRunResponse @@ -669,7 +807,7 @@ export type CreateDiscoveryRunResponse = Message<"lapp.web.v1.CreateDiscoveryRun * Use `create(CreateDiscoveryRunResponseSchema)` to create a new message. */ export const CreateDiscoveryRunResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 25); + messageDesc(file_lapp_web_v1_web, 29); /** * @generated from message lapp.web.v1.GetDiscoveryRunRequest @@ -686,7 +824,7 @@ export type GetDiscoveryRunRequest = Message<"lapp.web.v1.GetDiscoveryRunRequest * Use `create(GetDiscoveryRunRequestSchema)` to create a new message. */ export const GetDiscoveryRunRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 26); + messageDesc(file_lapp_web_v1_web, 30); /** * @generated from message lapp.web.v1.GetDiscoveryRunResponse @@ -703,7 +841,7 @@ export type GetDiscoveryRunResponse = Message<"lapp.web.v1.GetDiscoveryRunRespon * Use `create(GetDiscoveryRunResponseSchema)` to create a new message. */ export const GetDiscoveryRunResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 27); + messageDesc(file_lapp_web_v1_web, 31); /** * @generated from message lapp.web.v1.ListDiscoveryRunsRequest @@ -720,7 +858,7 @@ export type ListDiscoveryRunsRequest = Message<"lapp.web.v1.ListDiscoveryRunsReq * Use `create(ListDiscoveryRunsRequestSchema)` to create a new message. */ export const ListDiscoveryRunsRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 28); + messageDesc(file_lapp_web_v1_web, 32); /** * @generated from message lapp.web.v1.ListDiscoveryRunsResponse @@ -737,7 +875,120 @@ export type ListDiscoveryRunsResponse = Message<"lapp.web.v1.ListDiscoveryRunsRe * Use `create(ListDiscoveryRunsResponseSchema)` to create a new message. */ export const ListDiscoveryRunsResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 29); + messageDesc(file_lapp_web_v1_web, 33); + +/** + * @generated from message lapp.web.v1.ImportLogsRequest + */ +export type ImportLogsRequest = Message<"lapp.web.v1.ImportLogsRequest"> & { + /** + * @generated from field: string parent = 1; + */ + parent: string; + + /** + * @generated from oneof lapp.web.v1.ImportLogsRequest.source + */ + source: { + /** + * @generated from field: lapp.web.v1.GcpLoggingSource gcp_logging = 2; + */ + value: GcpLoggingSource; + case: "gcpLogging"; + } | { case: undefined; value?: undefined }; +}; + +/** + * Describes the message lapp.web.v1.ImportLogsRequest. + * Use `create(ImportLogsRequestSchema)` to create a new message. + */ +export const ImportLogsRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_lapp_web_v1_web, 34); + +/** + * @generated from message lapp.web.v1.ImportLogsResponse + */ +export type ImportLogsResponse = Message<"lapp.web.v1.ImportLogsResponse"> & { + /** + * @generated from field: lapp.web.v1.ImportRun import_run = 1; + */ + importRun?: ImportRun | undefined; +}; + +/** + * Describes the message lapp.web.v1.ImportLogsResponse. + * Use `create(ImportLogsResponseSchema)` to create a new message. + */ +export const ImportLogsResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_lapp_web_v1_web, 35); + +/** + * @generated from message lapp.web.v1.GetImportRunRequest + */ +export type GetImportRunRequest = Message<"lapp.web.v1.GetImportRunRequest"> & { + /** + * @generated from field: string name = 1; + */ + name: string; +}; + +/** + * Describes the message lapp.web.v1.GetImportRunRequest. + * Use `create(GetImportRunRequestSchema)` to create a new message. + */ +export const GetImportRunRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_lapp_web_v1_web, 36); + +/** + * @generated from message lapp.web.v1.GetImportRunResponse + */ +export type GetImportRunResponse = Message<"lapp.web.v1.GetImportRunResponse"> & { + /** + * @generated from field: lapp.web.v1.ImportRun import_run = 1; + */ + importRun?: ImportRun | undefined; +}; + +/** + * Describes the message lapp.web.v1.GetImportRunResponse. + * Use `create(GetImportRunResponseSchema)` to create a new message. + */ +export const GetImportRunResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_lapp_web_v1_web, 37); + +/** + * @generated from message lapp.web.v1.ListImportRunsRequest + */ +export type ListImportRunsRequest = Message<"lapp.web.v1.ListImportRunsRequest"> & { + /** + * @generated from field: string parent = 1; + */ + parent: string; +}; + +/** + * Describes the message lapp.web.v1.ListImportRunsRequest. + * Use `create(ListImportRunsRequestSchema)` to create a new message. + */ +export const ListImportRunsRequestSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_lapp_web_v1_web, 38); + +/** + * @generated from message lapp.web.v1.ListImportRunsResponse + */ +export type ListImportRunsResponse = Message<"lapp.web.v1.ListImportRunsResponse"> & { + /** + * @generated from field: repeated lapp.web.v1.ImportRun import_runs = 1; + */ + importRuns: ImportRun[]; +}; + +/** + * Describes the message lapp.web.v1.ListImportRunsResponse. + * Use `create(ListImportRunsResponseSchema)` to create a new message. + */ +export const ListImportRunsResponseSchema: GenMessage = /*@__PURE__*/ + messageDesc(file_lapp_web_v1_web, 39); /** * @generated from message lapp.web.v1.ListPatternsRequest @@ -754,7 +1005,7 @@ export type ListPatternsRequest = Message<"lapp.web.v1.ListPatternsRequest"> & { * Use `create(ListPatternsRequestSchema)` to create a new message. */ export const ListPatternsRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 30); + messageDesc(file_lapp_web_v1_web, 40); /** * @generated from message lapp.web.v1.ListPatternsResponse @@ -771,7 +1022,7 @@ export type ListPatternsResponse = Message<"lapp.web.v1.ListPatternsResponse"> & * Use `create(ListPatternsResponseSchema)` to create a new message. */ export const ListPatternsResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 31); + messageDesc(file_lapp_web_v1_web, 41); /** * @generated from message lapp.web.v1.GetPatternRequest @@ -788,7 +1039,7 @@ export type GetPatternRequest = Message<"lapp.web.v1.GetPatternRequest"> & { * Use `create(GetPatternRequestSchema)` to create a new message. */ export const GetPatternRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 32); + messageDesc(file_lapp_web_v1_web, 42); /** * @generated from message lapp.web.v1.GetPatternResponse @@ -805,7 +1056,7 @@ export type GetPatternResponse = Message<"lapp.web.v1.GetPatternResponse"> & { * Use `create(GetPatternResponseSchema)` to create a new message. */ export const GetPatternResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 33); + messageDesc(file_lapp_web_v1_web, 43); /** * @generated from message lapp.web.v1.GetErrorsViewRequest @@ -822,7 +1073,7 @@ export type GetErrorsViewRequest = Message<"lapp.web.v1.GetErrorsViewRequest"> & * Use `create(GetErrorsViewRequestSchema)` to create a new message. */ export const GetErrorsViewRequestSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 34); + messageDesc(file_lapp_web_v1_web, 44); /** * @generated from message lapp.web.v1.GetErrorsViewResponse @@ -844,7 +1095,7 @@ export type GetErrorsViewResponse = Message<"lapp.web.v1.GetErrorsViewResponse"> * Use `create(GetErrorsViewResponseSchema)` to create a new message. */ export const GetErrorsViewResponseSchema: GenMessage = /*@__PURE__*/ - messageDesc(file_lapp_web_v1_web, 35); + messageDesc(file_lapp_web_v1_web, 45); /** * @generated from enum lapp.web.v1.WorkspaceStatus @@ -959,6 +1210,42 @@ export enum DiscoveryStep { export const DiscoveryStepSchema: GenEnum = /*@__PURE__*/ enumDesc(file_lapp_web_v1_web, 2); +/** + * @generated from enum lapp.web.v1.ImportRunState + */ +export enum ImportRunState { + /** + * @generated from enum value: IMPORT_RUN_STATE_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + + /** + * @generated from enum value: IMPORT_RUN_STATE_QUEUED = 1; + */ + QUEUED = 1, + + /** + * @generated from enum value: IMPORT_RUN_STATE_RUNNING = 2; + */ + RUNNING = 2, + + /** + * @generated from enum value: IMPORT_RUN_STATE_SUCCEEDED = 3; + */ + SUCCEEDED = 3, + + /** + * @generated from enum value: IMPORT_RUN_STATE_FAILED = 4; + */ + FAILED = 4, +} + +/** + * Describes the enum lapp.web.v1.ImportRunState. + */ +export const ImportRunStateSchema: GenEnum = /*@__PURE__*/ + enumDesc(file_lapp_web_v1_web, 3); + /** * @generated from service lapp.web.v1.WorkspaceService */ @@ -1043,6 +1330,30 @@ export const WorkspaceService: GenService<{ input: typeof ListDiscoveryRunsRequestSchema; output: typeof ListDiscoveryRunsResponseSchema; }, + /** + * @generated from rpc lapp.web.v1.WorkspaceService.ImportLogs + */ + importLogs: { + methodKind: "unary"; + input: typeof ImportLogsRequestSchema; + output: typeof ImportLogsResponseSchema; + }, + /** + * @generated from rpc lapp.web.v1.WorkspaceService.GetImportRun + */ + getImportRun: { + methodKind: "unary"; + input: typeof GetImportRunRequestSchema; + output: typeof GetImportRunResponseSchema; + }, + /** + * @generated from rpc lapp.web.v1.WorkspaceService.ListImportRuns + */ + listImportRuns: { + methodKind: "unary"; + input: typeof ListImportRunsRequestSchema; + output: typeof ListImportRunsResponseSchema; + }, /** * @generated from rpc lapp.web.v1.WorkspaceService.ListPatterns */ diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx index b670f45..e11bb22 100644 --- a/frontend/src/main.tsx +++ b/frontend/src/main.tsx @@ -1,7 +1,8 @@ -import React, { useEffect, useMemo, useRef } from "react"; +import React, { useEffect, useMemo, useRef, useState } from "react"; import { createRoot } from "react-dom/client"; import { AlertTriangle, + CloudDownload, FileText, FolderOpen, Play, @@ -14,10 +15,12 @@ import { DiscoveryRun, DiscoveryRunState, DiscoveryStep, + ImportRun, + ImportRunState, Pattern, WorkspaceStatus } from "./gen/lapp/web/v1/web_pb"; -import { useAppStore } from "./store"; +import { GcpImportParams, useAppStore } from "./store"; import "./styles.css"; function App() { @@ -49,6 +52,11 @@ function App() { const uploadFiles = useAppStore((state) => state.uploadFiles); const deleteLogFile = useAppStore((state) => state.deleteLogFile); const startDiscovery = useAppStore((state) => state.startDiscovery); + const importDialogOpen = useAppStore((state) => state.importDialogOpen); + const activeImportRun = useAppStore((state) => state.activeImportRun); + const setImportDialogOpen = useAppStore((state) => state.setImportDialogOpen); + const startGcpImport = useAppStore((state) => state.startGcpImport); + const refreshActiveImportRun = useAppStore((state) => state.refreshActiveImportRun); const fileInputRef = useRef(null); const selectedWorkspace = useMemo( @@ -64,6 +72,7 @@ function App() { [patterns, selectedPatternName] ); const isDiscovering = selectedWorkspace?.status === WorkspaceStatus.DISCOVERING; + const isImporting = activeImportRun?.state === ImportRunState.RUNNING; useEffect(() => { void runAction(refreshWorkspaces); @@ -99,6 +108,20 @@ function App() { return () => window.clearInterval(timer); }, [refreshSelectedWorkspace, runAction, selectedRun?.name, selectedRun?.state]); + useEffect(() => { + if (!isImporting) { + return; + } + const timer = window.setInterval(() => { + void refreshActiveImportRun().catch((error: unknown) => { + void runAction(async () => { + throw error; + }); + }); + }, 1500); + return () => window.clearInterval(timer); + }, [isImporting, refreshActiveImportRun, runAction, activeImportRun?.name]); + return (
); } +function GcpImportDialog({ + busy, + onCancel, + onSubmit +}: { + busy: boolean; + onCancel: () => void; + onSubmit: (params: GcpImportParams) => void; +}) { + const [projectId, setProjectId] = useState(""); + const [filter, setFilter] = useState(""); + const [sinceHours, setSinceHours] = useState(1); + const [limit, setLimit] = useState(10000); + + return ( +
+
event.stopPropagation()} + onSubmit={(event) => { + event.preventDefault(); + onSubmit({ projectId: projectId.trim(), filter: filter.trim(), sinceHours, limit }); + }} + > +

Import from Google Cloud Logging

+

Fetches entries with local Application Default Credentials and adds them as a log file.

+ + +
+ + +
+
+ + +
+
+
+ ); +} + +function ImportStrip({ run }: { run: ImportRun }) { + const failed = run.state === ImportRunState.FAILED; + return ( +
+
+ {importStateLabel(run.state)} + {importRunMessage(run)} +
+
+ ); +} + +function importStateLabel(state: ImportRunState) { + return `GCP import ${ImportRunState[state]?.toLowerCase() ?? "unknown"}`; +} + +function importRunMessage(run: ImportRun) { + if (run.state === ImportRunState.FAILED) { + return run.error?.message || "Import failed without an error message."; + } + if (run.state === ImportRunState.SUCCEEDED) { + return `Imported ${run.entryCount} entries into ${run.logFileName}.`; + } + const fetched = run.progress?.fetchedCount ?? 0; + return fetched > 0 ? `Fetching entries, ${fetched} so far.` : "Fetching entries from Cloud Logging."; +} + function PatternRow({ pattern, error = false, diff --git a/frontend/src/store.ts b/frontend/src/store.ts index 993e3aa..8c01c6c 100644 --- a/frontend/src/store.ts +++ b/frontend/src/store.ts @@ -1,9 +1,12 @@ +import { timestampFromDate } from "@bufbuild/protobuf/wkt"; import { ConnectError } from "@connectrpc/connect"; import { create } from "zustand"; import { workspaceClient } from "./api"; import { DiscoveryRun, DiscoveryRunState, + ImportRun, + ImportRunState, LogFile, Pattern, UnmatchedErrorLine, @@ -12,6 +15,13 @@ import { export type Tab = "logs" | "patterns" | "errors"; +export type GcpImportParams = { + projectId: string; + filter: string; + sinceHours: number; + limit: number; +}; + type AppState = { workspaces: Workspace[]; selectedWorkspaceName: string; @@ -26,6 +36,8 @@ type AppState = { newWorkspaceID: string; notice: string; busy: boolean; + importDialogOpen: boolean; + activeImportRun: ImportRun | undefined; }; type AppActions = { @@ -44,6 +56,9 @@ type AppActions = { uploadFiles: (files: FileList | null) => Promise; deleteLogFile: (logFile: LogFile) => Promise; startDiscovery: () => Promise; + setImportDialogOpen: (open: boolean) => void; + startGcpImport: (params: GcpImportParams) => Promise; + refreshActiveImportRun: () => Promise; }; const emptyResults = { @@ -66,6 +81,8 @@ export const useAppStore = create()((set, get) => ({ newWorkspaceID: "", notice: "", busy: false, + importDialogOpen: false, + activeImportRun: undefined, setActiveTab: (activeTab) => set({ activeTab }), setNewWorkspaceID: (newWorkspaceID) => set({ newWorkspaceID }), @@ -76,6 +93,7 @@ export const useAppStore = create()((set, get) => ({ selectedPatternName: "", logFiles: [], discoveryRuns: [], + activeImportRun: undefined, ...emptyResults }), selectRun: (selectedRunName) => set({ selectedRunName, selectedPatternName: "" }), @@ -222,6 +240,41 @@ export const useAppStore = create()((set, get) => ({ set({ selectedRunName: response.discoveryRun?.name || "" }); await get().refreshWorkspaceDetails(selectedWorkspaceName); await get().refreshWorkspaces(); + }, + + setImportDialogOpen: (importDialogOpen) => set({ importDialogOpen }), + + startGcpImport: async (params) => { + const selectedWorkspaceName = get().selectedWorkspaceName; + if (!selectedWorkspaceName) return; + + const startTime = timestampFromDate(new Date(Date.now() - params.sinceHours * 3_600_000)); + const response = await workspaceClient.importLogs({ + parent: selectedWorkspaceName, + source: { + case: "gcpLogging", + value: { + projectId: params.projectId, + filter: params.filter, + startTime, + limit: params.limit + } + } + }); + set({ importDialogOpen: false, activeImportRun: response.importRun }); + }, + + refreshActiveImportRun: async () => { + const active = get().activeImportRun; + if (!active) return; + + const response = await workspaceClient.getImportRun({ name: active.name }); + const run = response.importRun; + set({ activeImportRun: run }); + if (run?.state === ImportRunState.SUCCEEDED) { + await get().refreshWorkspaceDetails(); + await get().refreshWorkspaces(); + } } })); diff --git a/frontend/src/styles.css b/frontend/src/styles.css index 95a3619..83e6805 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -433,6 +433,57 @@ th { color: #66768a; } +.dialog-backdrop { + position: fixed; + inset: 0; + display: grid; + place-items: center; + background: rgba(17, 24, 32, 0.45); + z-index: 10; +} + +.dialog { + display: grid; + gap: 12px; + width: min(480px, calc(100vw - 32px)); + border: 1px solid #d8e0e8; + border-radius: 10px; + background: #ffffff; + padding: 20px; +} + +.dialog h2 { + margin: 0; + font-size: 19px; +} + +.dialog p { + margin: 0; + color: #66768a; + font-size: 14px; +} + +.dialog label { + display: grid; + gap: 5px; + color: #526174; + font-size: 13px; + font-weight: 600; +} + +.dialog-row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 12px; +} + +.dialog footer { + display: flex; + justify-content: flex-end; + gap: 8px; + margin-top: 4px; +} + @media (max-width: 860px) { .app-shell { grid-template-columns: 1fr; diff --git a/gen/go/lapp/web/v1/web.pb.go b/gen/go/lapp/web/v1/web.pb.go index 1e461b1..2bf5f3e 100644 --- a/gen/go/lapp/web/v1/web.pb.go +++ b/gen/go/lapp/web/v1/web.pb.go @@ -190,6 +190,61 @@ func (DiscoveryStep) EnumDescriptor() ([]byte, []int) { return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{2} } +type ImportRunState int32 + +const ( + ImportRunState_IMPORT_RUN_STATE_UNSPECIFIED ImportRunState = 0 + ImportRunState_IMPORT_RUN_STATE_QUEUED ImportRunState = 1 + ImportRunState_IMPORT_RUN_STATE_RUNNING ImportRunState = 2 + ImportRunState_IMPORT_RUN_STATE_SUCCEEDED ImportRunState = 3 + ImportRunState_IMPORT_RUN_STATE_FAILED ImportRunState = 4 +) + +// Enum value maps for ImportRunState. +var ( + ImportRunState_name = map[int32]string{ + 0: "IMPORT_RUN_STATE_UNSPECIFIED", + 1: "IMPORT_RUN_STATE_QUEUED", + 2: "IMPORT_RUN_STATE_RUNNING", + 3: "IMPORT_RUN_STATE_SUCCEEDED", + 4: "IMPORT_RUN_STATE_FAILED", + } + ImportRunState_value = map[string]int32{ + "IMPORT_RUN_STATE_UNSPECIFIED": 0, + "IMPORT_RUN_STATE_QUEUED": 1, + "IMPORT_RUN_STATE_RUNNING": 2, + "IMPORT_RUN_STATE_SUCCEEDED": 3, + "IMPORT_RUN_STATE_FAILED": 4, + } +) + +func (x ImportRunState) Enum() *ImportRunState { + p := new(ImportRunState) + *p = x + return p +} + +func (x ImportRunState) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ImportRunState) Descriptor() protoreflect.EnumDescriptor { + return file_lapp_web_v1_web_proto_enumTypes[3].Descriptor() +} + +func (ImportRunState) Type() protoreflect.EnumType { + return &file_lapp_web_v1_web_proto_enumTypes[3] +} + +func (x ImportRunState) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ImportRunState.Descriptor instead. +func (ImportRunState) EnumDescriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{3} +} + type Workspace struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` @@ -698,36 +753,36 @@ func (x *DiscoveryRunError) GetStep() DiscoveryStep { return DiscoveryStep_DISCOVERY_STEP_UNSPECIFIED } -type Pattern struct { +type ImportRun struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - PatternId string `protobuf:"bytes,2,opt,name=pattern_id,json=patternId,proto3" json:"pattern_id,omitempty"` - SemanticId string `protobuf:"bytes,3,opt,name=semantic_id,json=semanticId,proto3" json:"semantic_id,omitempty"` - Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` - Template string `protobuf:"bytes,5,opt,name=template,proto3" json:"template,omitempty"` - Count int32 `protobuf:"varint,6,opt,name=count,proto3" json:"count,omitempty"` - FirstSeen *LineRef `protobuf:"bytes,7,opt,name=first_seen,json=firstSeen,proto3" json:"first_seen,omitempty"` - LastSeen *LineRef `protobuf:"bytes,8,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` - LineRefs []*LineRef `protobuf:"bytes,9,rep,name=line_refs,json=lineRefs,proto3" json:"line_refs,omitempty"` - Samples []string `protobuf:"bytes,10,rep,name=samples,proto3" json:"samples,omitempty"` + ImportRunId string `protobuf:"bytes,2,opt,name=import_run_id,json=importRunId,proto3" json:"import_run_id,omitempty"` + State ImportRunState `protobuf:"varint,3,opt,name=state,proto3,enum=lapp.web.v1.ImportRunState" json:"state,omitempty"` + Source string `protobuf:"bytes,4,opt,name=source,proto3" json:"source,omitempty"` + StartedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=started_at,json=startedAt,proto3" json:"started_at,omitempty"` + FinishedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=finished_at,json=finishedAt,proto3" json:"finished_at,omitempty"` + EntryCount int32 `protobuf:"varint,7,opt,name=entry_count,json=entryCount,proto3" json:"entry_count,omitempty"` + LogFileName string `protobuf:"bytes,8,opt,name=log_file_name,json=logFileName,proto3" json:"log_file_name,omitempty"` + Progress *ImportRunProgress `protobuf:"bytes,9,opt,name=progress,proto3" json:"progress,omitempty"` + Error *ImportRunError `protobuf:"bytes,10,opt,name=error,proto3" json:"error,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *Pattern) Reset() { - *x = Pattern{} +func (x *ImportRun) Reset() { + *x = ImportRun{} mi := &file_lapp_web_v1_web_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *Pattern) String() string { +func (x *ImportRun) String() string { return protoimpl.X.MessageStringOf(x) } -func (*Pattern) ProtoMessage() {} +func (*ImportRun) ProtoMessage() {} -func (x *Pattern) ProtoReflect() protoreflect.Message { +func (x *ImportRun) ProtoReflect() protoreflect.Message { mi := &file_lapp_web_v1_web_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -739,103 +794,102 @@ func (x *Pattern) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use Pattern.ProtoReflect.Descriptor instead. -func (*Pattern) Descriptor() ([]byte, []int) { +// Deprecated: Use ImportRun.ProtoReflect.Descriptor instead. +func (*ImportRun) Descriptor() ([]byte, []int) { return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{6} } -func (x *Pattern) GetName() string { +func (x *ImportRun) GetName() string { if x != nil { return x.Name } return "" } -func (x *Pattern) GetPatternId() string { +func (x *ImportRun) GetImportRunId() string { if x != nil { - return x.PatternId + return x.ImportRunId } return "" } -func (x *Pattern) GetSemanticId() string { +func (x *ImportRun) GetState() ImportRunState { if x != nil { - return x.SemanticId + return x.State } - return "" + return ImportRunState_IMPORT_RUN_STATE_UNSPECIFIED } -func (x *Pattern) GetDescription() string { +func (x *ImportRun) GetSource() string { if x != nil { - return x.Description + return x.Source } return "" } -func (x *Pattern) GetTemplate() string { +func (x *ImportRun) GetStartedAt() *timestamppb.Timestamp { if x != nil { - return x.Template + return x.StartedAt } - return "" + return nil } -func (x *Pattern) GetCount() int32 { +func (x *ImportRun) GetFinishedAt() *timestamppb.Timestamp { if x != nil { - return x.Count + return x.FinishedAt } - return 0 + return nil } -func (x *Pattern) GetFirstSeen() *LineRef { +func (x *ImportRun) GetEntryCount() int32 { if x != nil { - return x.FirstSeen + return x.EntryCount } - return nil + return 0 } -func (x *Pattern) GetLastSeen() *LineRef { +func (x *ImportRun) GetLogFileName() string { if x != nil { - return x.LastSeen + return x.LogFileName } - return nil + return "" } -func (x *Pattern) GetLineRefs() []*LineRef { +func (x *ImportRun) GetProgress() *ImportRunProgress { if x != nil { - return x.LineRefs + return x.Progress } return nil } -func (x *Pattern) GetSamples() []string { +func (x *ImportRun) GetError() *ImportRunError { if x != nil { - return x.Samples + return x.Error } return nil } -type LineRef struct { +type ImportRunProgress struct { state protoimpl.MessageState `protogen:"open.v1"` - FileName string `protobuf:"bytes,1,opt,name=file_name,json=fileName,proto3" json:"file_name,omitempty"` - LineNumber int32 `protobuf:"varint,2,opt,name=line_number,json=lineNumber,proto3" json:"line_number,omitempty"` + FetchedCount int32 `protobuf:"varint,1,opt,name=fetched_count,json=fetchedCount,proto3" json:"fetched_count,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *LineRef) Reset() { - *x = LineRef{} +func (x *ImportRunProgress) Reset() { + *x = ImportRunProgress{} mi := &file_lapp_web_v1_web_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *LineRef) String() string { +func (x *ImportRunProgress) String() string { return protoimpl.X.MessageStringOf(x) } -func (*LineRef) ProtoMessage() {} +func (*ImportRunProgress) ProtoMessage() {} -func (x *LineRef) ProtoReflect() protoreflect.Message { +func (x *ImportRunProgress) ProtoReflect() protoreflect.Message { mi := &file_lapp_web_v1_web_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -847,46 +901,40 @@ func (x *LineRef) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use LineRef.ProtoReflect.Descriptor instead. -func (*LineRef) Descriptor() ([]byte, []int) { +// Deprecated: Use ImportRunProgress.ProtoReflect.Descriptor instead. +func (*ImportRunProgress) Descriptor() ([]byte, []int) { return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{7} } -func (x *LineRef) GetFileName() string { - if x != nil { - return x.FileName - } - return "" -} - -func (x *LineRef) GetLineNumber() int32 { +func (x *ImportRunProgress) GetFetchedCount() int32 { if x != nil { - return x.LineNumber + return x.FetchedCount } return 0 } -type ErrorPattern struct { +type ImportRunError struct { state protoimpl.MessageState `protogen:"open.v1"` - Pattern *Pattern `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"` + Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *ErrorPattern) Reset() { - *x = ErrorPattern{} +func (x *ImportRunError) Reset() { + *x = ImportRunError{} mi := &file_lapp_web_v1_web_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *ErrorPattern) String() string { +func (x *ImportRunError) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ErrorPattern) ProtoMessage() {} +func (*ImportRunError) ProtoMessage() {} -func (x *ErrorPattern) ProtoReflect() protoreflect.Message { +func (x *ImportRunError) ProtoReflect() protoreflect.Message { mi := &file_lapp_web_v1_web_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -898,41 +946,50 @@ func (x *ErrorPattern) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ErrorPattern.ProtoReflect.Descriptor instead. -func (*ErrorPattern) Descriptor() ([]byte, []int) { +// Deprecated: Use ImportRunError.ProtoReflect.Descriptor instead. +func (*ImportRunError) Descriptor() ([]byte, []int) { return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{8} } -func (x *ErrorPattern) GetPattern() *Pattern { +func (x *ImportRunError) GetCode() string { if x != nil { - return x.Pattern + return x.Code } - return nil + return "" } -type UnmatchedErrorLine struct { +func (x *ImportRunError) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +type GcpLoggingSource struct { state protoimpl.MessageState `protogen:"open.v1"` - FileName string `protobuf:"bytes,1,opt,name=file_name,json=fileName,proto3" json:"file_name,omitempty"` - LineNumber int32 `protobuf:"varint,2,opt,name=line_number,json=lineNumber,proto3" json:"line_number,omitempty"` - Content string `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` + ProjectId string `protobuf:"bytes,1,opt,name=project_id,json=projectId,proto3" json:"project_id,omitempty"` + Filter string `protobuf:"bytes,2,opt,name=filter,proto3" json:"filter,omitempty"` + StartTime *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=start_time,json=startTime,proto3" json:"start_time,omitempty"` + EndTime *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=end_time,json=endTime,proto3" json:"end_time,omitempty"` + Limit int32 `protobuf:"varint,5,opt,name=limit,proto3" json:"limit,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *UnmatchedErrorLine) Reset() { - *x = UnmatchedErrorLine{} +func (x *GcpLoggingSource) Reset() { + *x = GcpLoggingSource{} mi := &file_lapp_web_v1_web_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *UnmatchedErrorLine) String() string { +func (x *GcpLoggingSource) String() string { return protoimpl.X.MessageStringOf(x) } -func (*UnmatchedErrorLine) ProtoMessage() {} +func (*GcpLoggingSource) ProtoMessage() {} -func (x *UnmatchedErrorLine) ProtoReflect() protoreflect.Message { +func (x *GcpLoggingSource) ProtoReflect() protoreflect.Message { mi := &file_lapp_web_v1_web_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -944,52 +1001,76 @@ func (x *UnmatchedErrorLine) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use UnmatchedErrorLine.ProtoReflect.Descriptor instead. -func (*UnmatchedErrorLine) Descriptor() ([]byte, []int) { +// Deprecated: Use GcpLoggingSource.ProtoReflect.Descriptor instead. +func (*GcpLoggingSource) Descriptor() ([]byte, []int) { return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{9} } -func (x *UnmatchedErrorLine) GetFileName() string { +func (x *GcpLoggingSource) GetProjectId() string { if x != nil { - return x.FileName + return x.ProjectId } return "" } -func (x *UnmatchedErrorLine) GetLineNumber() int32 { +func (x *GcpLoggingSource) GetFilter() string { if x != nil { - return x.LineNumber + return x.Filter } - return 0 + return "" } -func (x *UnmatchedErrorLine) GetContent() string { +func (x *GcpLoggingSource) GetStartTime() *timestamppb.Timestamp { if x != nil { - return x.Content + return x.StartTime } - return "" + return nil } -type ListWorkspacesRequest struct { +func (x *GcpLoggingSource) GetEndTime() *timestamppb.Timestamp { + if x != nil { + return x.EndTime + } + return nil +} + +func (x *GcpLoggingSource) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +type Pattern struct { state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + PatternId string `protobuf:"bytes,2,opt,name=pattern_id,json=patternId,proto3" json:"pattern_id,omitempty"` + SemanticId string `protobuf:"bytes,3,opt,name=semantic_id,json=semanticId,proto3" json:"semantic_id,omitempty"` + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + Template string `protobuf:"bytes,5,opt,name=template,proto3" json:"template,omitempty"` + Count int32 `protobuf:"varint,6,opt,name=count,proto3" json:"count,omitempty"` + FirstSeen *LineRef `protobuf:"bytes,7,opt,name=first_seen,json=firstSeen,proto3" json:"first_seen,omitempty"` + LastSeen *LineRef `protobuf:"bytes,8,opt,name=last_seen,json=lastSeen,proto3" json:"last_seen,omitempty"` + LineRefs []*LineRef `protobuf:"bytes,9,rep,name=line_refs,json=lineRefs,proto3" json:"line_refs,omitempty"` + Samples []string `protobuf:"bytes,10,rep,name=samples,proto3" json:"samples,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *ListWorkspacesRequest) Reset() { - *x = ListWorkspacesRequest{} +func (x *Pattern) Reset() { + *x = Pattern{} mi := &file_lapp_web_v1_web_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *ListWorkspacesRequest) String() string { +func (x *Pattern) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListWorkspacesRequest) ProtoMessage() {} +func (*Pattern) ProtoMessage() {} -func (x *ListWorkspacesRequest) ProtoReflect() protoreflect.Message { +func (x *Pattern) ProtoReflect() protoreflect.Message { mi := &file_lapp_web_v1_web_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1001,76 +1082,154 @@ func (x *ListWorkspacesRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListWorkspacesRequest.ProtoReflect.Descriptor instead. -func (*ListWorkspacesRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use Pattern.ProtoReflect.Descriptor instead. +func (*Pattern) Descriptor() ([]byte, []int) { return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{10} } -type ListWorkspacesResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - Workspaces []*Workspace `protobuf:"bytes,1,rep,name=workspaces,proto3" json:"workspaces,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache +func (x *Pattern) GetName() string { + if x != nil { + return x.Name + } + return "" } -func (x *ListWorkspacesResponse) Reset() { - *x = ListWorkspacesResponse{} - mi := &file_lapp_web_v1_web_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) +func (x *Pattern) GetPatternId() string { + if x != nil { + return x.PatternId + } + return "" } -func (x *ListWorkspacesResponse) String() string { - return protoimpl.X.MessageStringOf(x) +func (x *Pattern) GetSemanticId() string { + if x != nil { + return x.SemanticId + } + return "" } -func (*ListWorkspacesResponse) ProtoMessage() {} +func (x *Pattern) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} -func (x *ListWorkspacesResponse) ProtoReflect() protoreflect.Message { - mi := &file_lapp_web_v1_web_proto_msgTypes[11] +func (x *Pattern) GetTemplate() string { if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms + return x.Template } - return mi.MessageOf(x) + return "" } -// Deprecated: Use ListWorkspacesResponse.ProtoReflect.Descriptor instead. -func (*ListWorkspacesResponse) Descriptor() ([]byte, []int) { - return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{11} +func (x *Pattern) GetCount() int32 { + if x != nil { + return x.Count + } + return 0 } -func (x *ListWorkspacesResponse) GetWorkspaces() []*Workspace { +func (x *Pattern) GetFirstSeen() *LineRef { if x != nil { - return x.Workspaces + return x.FirstSeen } return nil } -type GetWorkspaceRequest struct { +func (x *Pattern) GetLastSeen() *LineRef { + if x != nil { + return x.LastSeen + } + return nil +} + +func (x *Pattern) GetLineRefs() []*LineRef { + if x != nil { + return x.LineRefs + } + return nil +} + +func (x *Pattern) GetSamples() []string { + if x != nil { + return x.Samples + } + return nil +} + +type LineRef struct { state protoimpl.MessageState `protogen:"open.v1"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + FileName string `protobuf:"bytes,1,opt,name=file_name,json=fileName,proto3" json:"file_name,omitempty"` + LineNumber int32 `protobuf:"varint,2,opt,name=line_number,json=lineNumber,proto3" json:"line_number,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *GetWorkspaceRequest) Reset() { - *x = GetWorkspaceRequest{} +func (x *LineRef) Reset() { + *x = LineRef{} + mi := &file_lapp_web_v1_web_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LineRef) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LineRef) ProtoMessage() {} + +func (x *LineRef) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LineRef.ProtoReflect.Descriptor instead. +func (*LineRef) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{11} +} + +func (x *LineRef) GetFileName() string { + if x != nil { + return x.FileName + } + return "" +} + +func (x *LineRef) GetLineNumber() int32 { + if x != nil { + return x.LineNumber + } + return 0 +} + +type ErrorPattern struct { + state protoimpl.MessageState `protogen:"open.v1"` + Pattern *Pattern `protobuf:"bytes,1,opt,name=pattern,proto3" json:"pattern,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ErrorPattern) Reset() { + *x = ErrorPattern{} mi := &file_lapp_web_v1_web_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *GetWorkspaceRequest) String() string { +func (x *ErrorPattern) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetWorkspaceRequest) ProtoMessage() {} +func (*ErrorPattern) ProtoMessage() {} -func (x *GetWorkspaceRequest) ProtoReflect() protoreflect.Message { +func (x *ErrorPattern) ProtoReflect() protoreflect.Message { mi := &file_lapp_web_v1_web_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1082,39 +1241,41 @@ func (x *GetWorkspaceRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetWorkspaceRequest.ProtoReflect.Descriptor instead. -func (*GetWorkspaceRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use ErrorPattern.ProtoReflect.Descriptor instead. +func (*ErrorPattern) Descriptor() ([]byte, []int) { return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{12} } -func (x *GetWorkspaceRequest) GetName() string { +func (x *ErrorPattern) GetPattern() *Pattern { if x != nil { - return x.Name + return x.Pattern } - return "" + return nil } -type GetWorkspaceResponse struct { +type UnmatchedErrorLine struct { state protoimpl.MessageState `protogen:"open.v1"` - Workspace *Workspace `protobuf:"bytes,1,opt,name=workspace,proto3" json:"workspace,omitempty"` + FileName string `protobuf:"bytes,1,opt,name=file_name,json=fileName,proto3" json:"file_name,omitempty"` + LineNumber int32 `protobuf:"varint,2,opt,name=line_number,json=lineNumber,proto3" json:"line_number,omitempty"` + Content string `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *GetWorkspaceResponse) Reset() { - *x = GetWorkspaceResponse{} +func (x *UnmatchedErrorLine) Reset() { + *x = UnmatchedErrorLine{} mi := &file_lapp_web_v1_web_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *GetWorkspaceResponse) String() string { +func (x *UnmatchedErrorLine) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetWorkspaceResponse) ProtoMessage() {} +func (*UnmatchedErrorLine) ProtoMessage() {} -func (x *GetWorkspaceResponse) ProtoReflect() protoreflect.Message { +func (x *UnmatchedErrorLine) ProtoReflect() protoreflect.Message { mi := &file_lapp_web_v1_web_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1126,39 +1287,52 @@ func (x *GetWorkspaceResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetWorkspaceResponse.ProtoReflect.Descriptor instead. -func (*GetWorkspaceResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use UnmatchedErrorLine.ProtoReflect.Descriptor instead. +func (*UnmatchedErrorLine) Descriptor() ([]byte, []int) { return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{13} } -func (x *GetWorkspaceResponse) GetWorkspace() *Workspace { +func (x *UnmatchedErrorLine) GetFileName() string { if x != nil { - return x.Workspace + return x.FileName } - return nil + return "" } -type CreateWorkspaceRequest struct { +func (x *UnmatchedErrorLine) GetLineNumber() int32 { + if x != nil { + return x.LineNumber + } + return 0 +} + +func (x *UnmatchedErrorLine) GetContent() string { + if x != nil { + return x.Content + } + return "" +} + +type ListWorkspacesRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - WorkspaceId string `protobuf:"bytes,1,opt,name=workspace_id,json=workspaceId,proto3" json:"workspace_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *CreateWorkspaceRequest) Reset() { - *x = CreateWorkspaceRequest{} +func (x *ListWorkspacesRequest) Reset() { + *x = ListWorkspacesRequest{} mi := &file_lapp_web_v1_web_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *CreateWorkspaceRequest) String() string { +func (x *ListWorkspacesRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CreateWorkspaceRequest) ProtoMessage() {} +func (*ListWorkspacesRequest) ProtoMessage() {} -func (x *CreateWorkspaceRequest) ProtoReflect() protoreflect.Message { +func (x *ListWorkspacesRequest) ProtoReflect() protoreflect.Message { mi := &file_lapp_web_v1_web_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1170,39 +1344,32 @@ func (x *CreateWorkspaceRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CreateWorkspaceRequest.ProtoReflect.Descriptor instead. -func (*CreateWorkspaceRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use ListWorkspacesRequest.ProtoReflect.Descriptor instead. +func (*ListWorkspacesRequest) Descriptor() ([]byte, []int) { return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{14} } -func (x *CreateWorkspaceRequest) GetWorkspaceId() string { - if x != nil { - return x.WorkspaceId - } - return "" -} - -type CreateWorkspaceResponse struct { +type ListWorkspacesResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - Workspace *Workspace `protobuf:"bytes,1,opt,name=workspace,proto3" json:"workspace,omitempty"` + Workspaces []*Workspace `protobuf:"bytes,1,rep,name=workspaces,proto3" json:"workspaces,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *CreateWorkspaceResponse) Reset() { - *x = CreateWorkspaceResponse{} +func (x *ListWorkspacesResponse) Reset() { + *x = ListWorkspacesResponse{} mi := &file_lapp_web_v1_web_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *CreateWorkspaceResponse) String() string { +func (x *ListWorkspacesResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CreateWorkspaceResponse) ProtoMessage() {} +func (*ListWorkspacesResponse) ProtoMessage() {} -func (x *CreateWorkspaceResponse) ProtoReflect() protoreflect.Message { +func (x *ListWorkspacesResponse) ProtoReflect() protoreflect.Message { mi := &file_lapp_web_v1_web_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1214,39 +1381,39 @@ func (x *CreateWorkspaceResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CreateWorkspaceResponse.ProtoReflect.Descriptor instead. -func (*CreateWorkspaceResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use ListWorkspacesResponse.ProtoReflect.Descriptor instead. +func (*ListWorkspacesResponse) Descriptor() ([]byte, []int) { return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{15} } -func (x *CreateWorkspaceResponse) GetWorkspace() *Workspace { +func (x *ListWorkspacesResponse) GetWorkspaces() []*Workspace { if x != nil { - return x.Workspace + return x.Workspaces } return nil } -type DeleteWorkspaceRequest struct { +type GetWorkspaceRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *DeleteWorkspaceRequest) Reset() { - *x = DeleteWorkspaceRequest{} +func (x *GetWorkspaceRequest) Reset() { + *x = GetWorkspaceRequest{} mi := &file_lapp_web_v1_web_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *DeleteWorkspaceRequest) String() string { +func (x *GetWorkspaceRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeleteWorkspaceRequest) ProtoMessage() {} +func (*GetWorkspaceRequest) ProtoMessage() {} -func (x *DeleteWorkspaceRequest) ProtoReflect() protoreflect.Message { +func (x *GetWorkspaceRequest) ProtoReflect() protoreflect.Message { mi := &file_lapp_web_v1_web_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1258,38 +1425,39 @@ func (x *DeleteWorkspaceRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DeleteWorkspaceRequest.ProtoReflect.Descriptor instead. -func (*DeleteWorkspaceRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use GetWorkspaceRequest.ProtoReflect.Descriptor instead. +func (*GetWorkspaceRequest) Descriptor() ([]byte, []int) { return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{16} } -func (x *DeleteWorkspaceRequest) GetName() string { +func (x *GetWorkspaceRequest) GetName() string { if x != nil { return x.Name } return "" } -type DeleteWorkspaceResponse struct { +type GetWorkspaceResponse struct { state protoimpl.MessageState `protogen:"open.v1"` + Workspace *Workspace `protobuf:"bytes,1,opt,name=workspace,proto3" json:"workspace,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *DeleteWorkspaceResponse) Reset() { - *x = DeleteWorkspaceResponse{} +func (x *GetWorkspaceResponse) Reset() { + *x = GetWorkspaceResponse{} mi := &file_lapp_web_v1_web_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *DeleteWorkspaceResponse) String() string { +func (x *GetWorkspaceResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeleteWorkspaceResponse) ProtoMessage() {} +func (*GetWorkspaceResponse) ProtoMessage() {} -func (x *DeleteWorkspaceResponse) ProtoReflect() protoreflect.Message { +func (x *GetWorkspaceResponse) ProtoReflect() protoreflect.Message { mi := &file_lapp_web_v1_web_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1301,32 +1469,39 @@ func (x *DeleteWorkspaceResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DeleteWorkspaceResponse.ProtoReflect.Descriptor instead. -func (*DeleteWorkspaceResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use GetWorkspaceResponse.ProtoReflect.Descriptor instead. +func (*GetWorkspaceResponse) Descriptor() ([]byte, []int) { return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{17} } -type ListLogFilesRequest struct { +func (x *GetWorkspaceResponse) GetWorkspace() *Workspace { + if x != nil { + return x.Workspace + } + return nil +} + +type CreateWorkspaceRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` + WorkspaceId string `protobuf:"bytes,1,opt,name=workspace_id,json=workspaceId,proto3" json:"workspace_id,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *ListLogFilesRequest) Reset() { - *x = ListLogFilesRequest{} +func (x *CreateWorkspaceRequest) Reset() { + *x = CreateWorkspaceRequest{} mi := &file_lapp_web_v1_web_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *ListLogFilesRequest) String() string { +func (x *CreateWorkspaceRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListLogFilesRequest) ProtoMessage() {} +func (*CreateWorkspaceRequest) ProtoMessage() {} -func (x *ListLogFilesRequest) ProtoReflect() protoreflect.Message { +func (x *CreateWorkspaceRequest) ProtoReflect() protoreflect.Message { mi := &file_lapp_web_v1_web_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1338,39 +1513,39 @@ func (x *ListLogFilesRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListLogFilesRequest.ProtoReflect.Descriptor instead. -func (*ListLogFilesRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use CreateWorkspaceRequest.ProtoReflect.Descriptor instead. +func (*CreateWorkspaceRequest) Descriptor() ([]byte, []int) { return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{18} } -func (x *ListLogFilesRequest) GetParent() string { +func (x *CreateWorkspaceRequest) GetWorkspaceId() string { if x != nil { - return x.Parent + return x.WorkspaceId } return "" } -type ListLogFilesResponse struct { +type CreateWorkspaceResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - LogFiles []*LogFile `protobuf:"bytes,1,rep,name=log_files,json=logFiles,proto3" json:"log_files,omitempty"` + Workspace *Workspace `protobuf:"bytes,1,opt,name=workspace,proto3" json:"workspace,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *ListLogFilesResponse) Reset() { - *x = ListLogFilesResponse{} +func (x *CreateWorkspaceResponse) Reset() { + *x = CreateWorkspaceResponse{} mi := &file_lapp_web_v1_web_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *ListLogFilesResponse) String() string { +func (x *CreateWorkspaceResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListLogFilesResponse) ProtoMessage() {} +func (*CreateWorkspaceResponse) ProtoMessage() {} -func (x *ListLogFilesResponse) ProtoReflect() protoreflect.Message { +func (x *CreateWorkspaceResponse) ProtoReflect() protoreflect.Message { mi := &file_lapp_web_v1_web_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -1382,42 +1557,488 @@ func (x *ListLogFilesResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListLogFilesResponse.ProtoReflect.Descriptor instead. -func (*ListLogFilesResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use CreateWorkspaceResponse.ProtoReflect.Descriptor instead. +func (*CreateWorkspaceResponse) Descriptor() ([]byte, []int) { return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{19} } -func (x *ListLogFilesResponse) GetLogFiles() []*LogFile { +func (x *CreateWorkspaceResponse) GetWorkspace() *Workspace { if x != nil { - return x.LogFiles + return x.Workspace } return nil } -type UploadLogFileRequest struct { +type DeleteWorkspaceRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteWorkspaceRequest) Reset() { + *x = DeleteWorkspaceRequest{} + mi := &file_lapp_web_v1_web_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteWorkspaceRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteWorkspaceRequest) ProtoMessage() {} + +func (x *DeleteWorkspaceRequest) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[20] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteWorkspaceRequest.ProtoReflect.Descriptor instead. +func (*DeleteWorkspaceRequest) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{20} +} + +func (x *DeleteWorkspaceRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type DeleteWorkspaceResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteWorkspaceResponse) Reset() { + *x = DeleteWorkspaceResponse{} + mi := &file_lapp_web_v1_web_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteWorkspaceResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteWorkspaceResponse) ProtoMessage() {} + +func (x *DeleteWorkspaceResponse) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[21] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteWorkspaceResponse.ProtoReflect.Descriptor instead. +func (*DeleteWorkspaceResponse) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{21} +} + +type ListLogFilesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListLogFilesRequest) Reset() { + *x = ListLogFilesRequest{} + mi := &file_lapp_web_v1_web_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListLogFilesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListLogFilesRequest) ProtoMessage() {} + +func (x *ListLogFilesRequest) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[22] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListLogFilesRequest.ProtoReflect.Descriptor instead. +func (*ListLogFilesRequest) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{22} +} + +func (x *ListLogFilesRequest) GetParent() string { + if x != nil { + return x.Parent + } + return "" +} + +type ListLogFilesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + LogFiles []*LogFile `protobuf:"bytes,1,rep,name=log_files,json=logFiles,proto3" json:"log_files,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListLogFilesResponse) Reset() { + *x = ListLogFilesResponse{} + mi := &file_lapp_web_v1_web_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListLogFilesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListLogFilesResponse) ProtoMessage() {} + +func (x *ListLogFilesResponse) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[23] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListLogFilesResponse.ProtoReflect.Descriptor instead. +func (*ListLogFilesResponse) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{23} +} + +func (x *ListLogFilesResponse) GetLogFiles() []*LogFile { + if x != nil { + return x.LogFiles + } + return nil +} + +type UploadLogFileRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` + FileName string `protobuf:"bytes,2,opt,name=file_name,json=fileName,proto3" json:"file_name,omitempty"` + Content []byte `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UploadLogFileRequest) Reset() { + *x = UploadLogFileRequest{} + mi := &file_lapp_web_v1_web_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UploadLogFileRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UploadLogFileRequest) ProtoMessage() {} + +func (x *UploadLogFileRequest) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[24] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UploadLogFileRequest.ProtoReflect.Descriptor instead. +func (*UploadLogFileRequest) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{24} +} + +func (x *UploadLogFileRequest) GetParent() string { + if x != nil { + return x.Parent + } + return "" +} + +func (x *UploadLogFileRequest) GetFileName() string { + if x != nil { + return x.FileName + } + return "" +} + +func (x *UploadLogFileRequest) GetContent() []byte { + if x != nil { + return x.Content + } + return nil +} + +type UploadLogFileResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + LogFile *LogFile `protobuf:"bytes,1,opt,name=log_file,json=logFile,proto3" json:"log_file,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UploadLogFileResponse) Reset() { + *x = UploadLogFileResponse{} + mi := &file_lapp_web_v1_web_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UploadLogFileResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UploadLogFileResponse) ProtoMessage() {} + +func (x *UploadLogFileResponse) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[25] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UploadLogFileResponse.ProtoReflect.Descriptor instead. +func (*UploadLogFileResponse) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{25} +} + +func (x *UploadLogFileResponse) GetLogFile() *LogFile { + if x != nil { + return x.LogFile + } + return nil +} + +type DeleteLogFileRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteLogFileRequest) Reset() { + *x = DeleteLogFileRequest{} + mi := &file_lapp_web_v1_web_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteLogFileRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteLogFileRequest) ProtoMessage() {} + +func (x *DeleteLogFileRequest) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[26] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteLogFileRequest.ProtoReflect.Descriptor instead. +func (*DeleteLogFileRequest) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{26} +} + +func (x *DeleteLogFileRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type DeleteLogFileResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteLogFileResponse) Reset() { + *x = DeleteLogFileResponse{} + mi := &file_lapp_web_v1_web_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteLogFileResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteLogFileResponse) ProtoMessage() {} + +func (x *DeleteLogFileResponse) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[27] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteLogFileResponse.ProtoReflect.Descriptor instead. +func (*DeleteLogFileResponse) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{27} +} + +type CreateDiscoveryRunRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` + Model string `protobuf:"bytes,2,opt,name=model,proto3" json:"model,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateDiscoveryRunRequest) Reset() { + *x = CreateDiscoveryRunRequest{} + mi := &file_lapp_web_v1_web_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateDiscoveryRunRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateDiscoveryRunRequest) ProtoMessage() {} + +func (x *CreateDiscoveryRunRequest) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[28] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateDiscoveryRunRequest.ProtoReflect.Descriptor instead. +func (*CreateDiscoveryRunRequest) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{28} +} + +func (x *CreateDiscoveryRunRequest) GetParent() string { + if x != nil { + return x.Parent + } + return "" +} + +func (x *CreateDiscoveryRunRequest) GetModel() string { + if x != nil { + return x.Model + } + return "" +} + +type CreateDiscoveryRunResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + DiscoveryRun *DiscoveryRun `protobuf:"bytes,1,opt,name=discovery_run,json=discoveryRun,proto3" json:"discovery_run,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateDiscoveryRunResponse) Reset() { + *x = CreateDiscoveryRunResponse{} + mi := &file_lapp_web_v1_web_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateDiscoveryRunResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateDiscoveryRunResponse) ProtoMessage() {} + +func (x *CreateDiscoveryRunResponse) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[29] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateDiscoveryRunResponse.ProtoReflect.Descriptor instead. +func (*CreateDiscoveryRunResponse) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{29} +} + +func (x *CreateDiscoveryRunResponse) GetDiscoveryRun() *DiscoveryRun { + if x != nil { + return x.DiscoveryRun + } + return nil +} + +type GetDiscoveryRunRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` - FileName string `protobuf:"bytes,2,opt,name=file_name,json=fileName,proto3" json:"file_name,omitempty"` - Content []byte `protobuf:"bytes,3,opt,name=content,proto3" json:"content,omitempty"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *UploadLogFileRequest) Reset() { - *x = UploadLogFileRequest{} - mi := &file_lapp_web_v1_web_proto_msgTypes[20] +func (x *GetDiscoveryRunRequest) Reset() { + *x = GetDiscoveryRunRequest{} + mi := &file_lapp_web_v1_web_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *UploadLogFileRequest) String() string { +func (x *GetDiscoveryRunRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*UploadLogFileRequest) ProtoMessage() {} +func (*GetDiscoveryRunRequest) ProtoMessage() {} -func (x *UploadLogFileRequest) ProtoReflect() protoreflect.Message { - mi := &file_lapp_web_v1_web_proto_msgTypes[20] +func (x *GetDiscoveryRunRequest) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[30] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1428,54 +2049,40 @@ func (x *UploadLogFileRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use UploadLogFileRequest.ProtoReflect.Descriptor instead. -func (*UploadLogFileRequest) Descriptor() ([]byte, []int) { - return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{20} -} - -func (x *UploadLogFileRequest) GetParent() string { - if x != nil { - return x.Parent - } - return "" +// Deprecated: Use GetDiscoveryRunRequest.ProtoReflect.Descriptor instead. +func (*GetDiscoveryRunRequest) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{30} } -func (x *UploadLogFileRequest) GetFileName() string { +func (x *GetDiscoveryRunRequest) GetName() string { if x != nil { - return x.FileName + return x.Name } return "" } -func (x *UploadLogFileRequest) GetContent() []byte { - if x != nil { - return x.Content - } - return nil -} - -type UploadLogFileResponse struct { +type GetDiscoveryRunResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - LogFile *LogFile `protobuf:"bytes,1,opt,name=log_file,json=logFile,proto3" json:"log_file,omitempty"` + DiscoveryRun *DiscoveryRun `protobuf:"bytes,1,opt,name=discovery_run,json=discoveryRun,proto3" json:"discovery_run,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *UploadLogFileResponse) Reset() { - *x = UploadLogFileResponse{} - mi := &file_lapp_web_v1_web_proto_msgTypes[21] +func (x *GetDiscoveryRunResponse) Reset() { + *x = GetDiscoveryRunResponse{} + mi := &file_lapp_web_v1_web_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *UploadLogFileResponse) String() string { +func (x *GetDiscoveryRunResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*UploadLogFileResponse) ProtoMessage() {} +func (*GetDiscoveryRunResponse) ProtoMessage() {} -func (x *UploadLogFileResponse) ProtoReflect() protoreflect.Message { - mi := &file_lapp_web_v1_web_proto_msgTypes[21] +func (x *GetDiscoveryRunResponse) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[31] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1486,40 +2093,40 @@ func (x *UploadLogFileResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use UploadLogFileResponse.ProtoReflect.Descriptor instead. -func (*UploadLogFileResponse) Descriptor() ([]byte, []int) { - return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{21} +// Deprecated: Use GetDiscoveryRunResponse.ProtoReflect.Descriptor instead. +func (*GetDiscoveryRunResponse) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{31} } -func (x *UploadLogFileResponse) GetLogFile() *LogFile { +func (x *GetDiscoveryRunResponse) GetDiscoveryRun() *DiscoveryRun { if x != nil { - return x.LogFile + return x.DiscoveryRun } return nil } -type DeleteLogFileRequest struct { +type ListDiscoveryRunsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *DeleteLogFileRequest) Reset() { - *x = DeleteLogFileRequest{} - mi := &file_lapp_web_v1_web_proto_msgTypes[22] +func (x *ListDiscoveryRunsRequest) Reset() { + *x = ListDiscoveryRunsRequest{} + mi := &file_lapp_web_v1_web_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *DeleteLogFileRequest) String() string { +func (x *ListDiscoveryRunsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeleteLogFileRequest) ProtoMessage() {} +func (*ListDiscoveryRunsRequest) ProtoMessage() {} -func (x *DeleteLogFileRequest) ProtoReflect() protoreflect.Message { - mi := &file_lapp_web_v1_web_proto_msgTypes[22] +func (x *ListDiscoveryRunsRequest) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[32] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1530,39 +2137,40 @@ func (x *DeleteLogFileRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DeleteLogFileRequest.ProtoReflect.Descriptor instead. -func (*DeleteLogFileRequest) Descriptor() ([]byte, []int) { - return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{22} +// Deprecated: Use ListDiscoveryRunsRequest.ProtoReflect.Descriptor instead. +func (*ListDiscoveryRunsRequest) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{32} } -func (x *DeleteLogFileRequest) GetName() string { +func (x *ListDiscoveryRunsRequest) GetParent() string { if x != nil { - return x.Name + return x.Parent } return "" } -type DeleteLogFileResponse struct { +type ListDiscoveryRunsResponse struct { state protoimpl.MessageState `protogen:"open.v1"` + DiscoveryRuns []*DiscoveryRun `protobuf:"bytes,1,rep,name=discovery_runs,json=discoveryRuns,proto3" json:"discovery_runs,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *DeleteLogFileResponse) Reset() { - *x = DeleteLogFileResponse{} - mi := &file_lapp_web_v1_web_proto_msgTypes[23] +func (x *ListDiscoveryRunsResponse) Reset() { + *x = ListDiscoveryRunsResponse{} + mi := &file_lapp_web_v1_web_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *DeleteLogFileResponse) String() string { +func (x *ListDiscoveryRunsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DeleteLogFileResponse) ProtoMessage() {} +func (*ListDiscoveryRunsResponse) ProtoMessage() {} -func (x *DeleteLogFileResponse) ProtoReflect() protoreflect.Message { - mi := &file_lapp_web_v1_web_proto_msgTypes[23] +func (x *ListDiscoveryRunsResponse) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[33] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1573,34 +2181,44 @@ func (x *DeleteLogFileResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DeleteLogFileResponse.ProtoReflect.Descriptor instead. -func (*DeleteLogFileResponse) Descriptor() ([]byte, []int) { - return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{23} +// Deprecated: Use ListDiscoveryRunsResponse.ProtoReflect.Descriptor instead. +func (*ListDiscoveryRunsResponse) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{33} } -type CreateDiscoveryRunRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` - Model string `protobuf:"bytes,2,opt,name=model,proto3" json:"model,omitempty"` +func (x *ListDiscoveryRunsResponse) GetDiscoveryRuns() []*DiscoveryRun { + if x != nil { + return x.DiscoveryRuns + } + return nil +} + +type ImportLogsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` + // Types that are valid to be assigned to Source: + // + // *ImportLogsRequest_GcpLogging + Source isImportLogsRequest_Source `protobuf_oneof:"source"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *CreateDiscoveryRunRequest) Reset() { - *x = CreateDiscoveryRunRequest{} - mi := &file_lapp_web_v1_web_proto_msgTypes[24] +func (x *ImportLogsRequest) Reset() { + *x = ImportLogsRequest{} + mi := &file_lapp_web_v1_web_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *CreateDiscoveryRunRequest) String() string { +func (x *ImportLogsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CreateDiscoveryRunRequest) ProtoMessage() {} +func (*ImportLogsRequest) ProtoMessage() {} -func (x *CreateDiscoveryRunRequest) ProtoReflect() protoreflect.Message { - mi := &file_lapp_web_v1_web_proto_msgTypes[24] +func (x *ImportLogsRequest) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[34] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1611,47 +2229,66 @@ func (x *CreateDiscoveryRunRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CreateDiscoveryRunRequest.ProtoReflect.Descriptor instead. -func (*CreateDiscoveryRunRequest) Descriptor() ([]byte, []int) { - return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{24} +// Deprecated: Use ImportLogsRequest.ProtoReflect.Descriptor instead. +func (*ImportLogsRequest) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{34} } -func (x *CreateDiscoveryRunRequest) GetParent() string { +func (x *ImportLogsRequest) GetParent() string { if x != nil { return x.Parent } return "" } -func (x *CreateDiscoveryRunRequest) GetModel() string { +func (x *ImportLogsRequest) GetSource() isImportLogsRequest_Source { if x != nil { - return x.Model + return x.Source } - return "" + return nil } -type CreateDiscoveryRunResponse struct { +func (x *ImportLogsRequest) GetGcpLogging() *GcpLoggingSource { + if x != nil { + if x, ok := x.Source.(*ImportLogsRequest_GcpLogging); ok { + return x.GcpLogging + } + } + return nil +} + +type isImportLogsRequest_Source interface { + isImportLogsRequest_Source() +} + +type ImportLogsRequest_GcpLogging struct { + GcpLogging *GcpLoggingSource `protobuf:"bytes,2,opt,name=gcp_logging,json=gcpLogging,proto3,oneof"` +} + +func (*ImportLogsRequest_GcpLogging) isImportLogsRequest_Source() {} + +type ImportLogsResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - DiscoveryRun *DiscoveryRun `protobuf:"bytes,1,opt,name=discovery_run,json=discoveryRun,proto3" json:"discovery_run,omitempty"` + ImportRun *ImportRun `protobuf:"bytes,1,opt,name=import_run,json=importRun,proto3" json:"import_run,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *CreateDiscoveryRunResponse) Reset() { - *x = CreateDiscoveryRunResponse{} - mi := &file_lapp_web_v1_web_proto_msgTypes[25] +func (x *ImportLogsResponse) Reset() { + *x = ImportLogsResponse{} + mi := &file_lapp_web_v1_web_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *CreateDiscoveryRunResponse) String() string { +func (x *ImportLogsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CreateDiscoveryRunResponse) ProtoMessage() {} +func (*ImportLogsResponse) ProtoMessage() {} -func (x *CreateDiscoveryRunResponse) ProtoReflect() protoreflect.Message { - mi := &file_lapp_web_v1_web_proto_msgTypes[25] +func (x *ImportLogsResponse) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[35] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1662,40 +2299,40 @@ func (x *CreateDiscoveryRunResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CreateDiscoveryRunResponse.ProtoReflect.Descriptor instead. -func (*CreateDiscoveryRunResponse) Descriptor() ([]byte, []int) { - return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{25} +// Deprecated: Use ImportLogsResponse.ProtoReflect.Descriptor instead. +func (*ImportLogsResponse) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{35} } -func (x *CreateDiscoveryRunResponse) GetDiscoveryRun() *DiscoveryRun { +func (x *ImportLogsResponse) GetImportRun() *ImportRun { if x != nil { - return x.DiscoveryRun + return x.ImportRun } return nil } -type GetDiscoveryRunRequest struct { +type GetImportRunRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *GetDiscoveryRunRequest) Reset() { - *x = GetDiscoveryRunRequest{} - mi := &file_lapp_web_v1_web_proto_msgTypes[26] +func (x *GetImportRunRequest) Reset() { + *x = GetImportRunRequest{} + mi := &file_lapp_web_v1_web_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *GetDiscoveryRunRequest) String() string { +func (x *GetImportRunRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetDiscoveryRunRequest) ProtoMessage() {} +func (*GetImportRunRequest) ProtoMessage() {} -func (x *GetDiscoveryRunRequest) ProtoReflect() protoreflect.Message { - mi := &file_lapp_web_v1_web_proto_msgTypes[26] +func (x *GetImportRunRequest) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[36] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1706,40 +2343,40 @@ func (x *GetDiscoveryRunRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetDiscoveryRunRequest.ProtoReflect.Descriptor instead. -func (*GetDiscoveryRunRequest) Descriptor() ([]byte, []int) { - return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{26} +// Deprecated: Use GetImportRunRequest.ProtoReflect.Descriptor instead. +func (*GetImportRunRequest) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{36} } -func (x *GetDiscoveryRunRequest) GetName() string { +func (x *GetImportRunRequest) GetName() string { if x != nil { return x.Name } return "" } -type GetDiscoveryRunResponse struct { +type GetImportRunResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - DiscoveryRun *DiscoveryRun `protobuf:"bytes,1,opt,name=discovery_run,json=discoveryRun,proto3" json:"discovery_run,omitempty"` + ImportRun *ImportRun `protobuf:"bytes,1,opt,name=import_run,json=importRun,proto3" json:"import_run,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *GetDiscoveryRunResponse) Reset() { - *x = GetDiscoveryRunResponse{} - mi := &file_lapp_web_v1_web_proto_msgTypes[27] +func (x *GetImportRunResponse) Reset() { + *x = GetImportRunResponse{} + mi := &file_lapp_web_v1_web_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *GetDiscoveryRunResponse) String() string { +func (x *GetImportRunResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*GetDiscoveryRunResponse) ProtoMessage() {} +func (*GetImportRunResponse) ProtoMessage() {} -func (x *GetDiscoveryRunResponse) ProtoReflect() protoreflect.Message { - mi := &file_lapp_web_v1_web_proto_msgTypes[27] +func (x *GetImportRunResponse) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[37] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1750,40 +2387,40 @@ func (x *GetDiscoveryRunResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use GetDiscoveryRunResponse.ProtoReflect.Descriptor instead. -func (*GetDiscoveryRunResponse) Descriptor() ([]byte, []int) { - return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{27} +// Deprecated: Use GetImportRunResponse.ProtoReflect.Descriptor instead. +func (*GetImportRunResponse) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{37} } -func (x *GetDiscoveryRunResponse) GetDiscoveryRun() *DiscoveryRun { +func (x *GetImportRunResponse) GetImportRun() *ImportRun { if x != nil { - return x.DiscoveryRun + return x.ImportRun } return nil } -type ListDiscoveryRunsRequest struct { +type ListImportRunsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Parent string `protobuf:"bytes,1,opt,name=parent,proto3" json:"parent,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *ListDiscoveryRunsRequest) Reset() { - *x = ListDiscoveryRunsRequest{} - mi := &file_lapp_web_v1_web_proto_msgTypes[28] +func (x *ListImportRunsRequest) Reset() { + *x = ListImportRunsRequest{} + mi := &file_lapp_web_v1_web_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *ListDiscoveryRunsRequest) String() string { +func (x *ListImportRunsRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListDiscoveryRunsRequest) ProtoMessage() {} +func (*ListImportRunsRequest) ProtoMessage() {} -func (x *ListDiscoveryRunsRequest) ProtoReflect() protoreflect.Message { - mi := &file_lapp_web_v1_web_proto_msgTypes[28] +func (x *ListImportRunsRequest) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[38] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1794,40 +2431,40 @@ func (x *ListDiscoveryRunsRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListDiscoveryRunsRequest.ProtoReflect.Descriptor instead. -func (*ListDiscoveryRunsRequest) Descriptor() ([]byte, []int) { - return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{28} +// Deprecated: Use ListImportRunsRequest.ProtoReflect.Descriptor instead. +func (*ListImportRunsRequest) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{38} } -func (x *ListDiscoveryRunsRequest) GetParent() string { +func (x *ListImportRunsRequest) GetParent() string { if x != nil { return x.Parent } return "" } -type ListDiscoveryRunsResponse struct { +type ListImportRunsResponse struct { state protoimpl.MessageState `protogen:"open.v1"` - DiscoveryRuns []*DiscoveryRun `protobuf:"bytes,1,rep,name=discovery_runs,json=discoveryRuns,proto3" json:"discovery_runs,omitempty"` + ImportRuns []*ImportRun `protobuf:"bytes,1,rep,name=import_runs,json=importRuns,proto3" json:"import_runs,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *ListDiscoveryRunsResponse) Reset() { - *x = ListDiscoveryRunsResponse{} - mi := &file_lapp_web_v1_web_proto_msgTypes[29] +func (x *ListImportRunsResponse) Reset() { + *x = ListImportRunsResponse{} + mi := &file_lapp_web_v1_web_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *ListDiscoveryRunsResponse) String() string { +func (x *ListImportRunsResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ListDiscoveryRunsResponse) ProtoMessage() {} +func (*ListImportRunsResponse) ProtoMessage() {} -func (x *ListDiscoveryRunsResponse) ProtoReflect() protoreflect.Message { - mi := &file_lapp_web_v1_web_proto_msgTypes[29] +func (x *ListImportRunsResponse) ProtoReflect() protoreflect.Message { + mi := &file_lapp_web_v1_web_proto_msgTypes[39] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1838,14 +2475,14 @@ func (x *ListDiscoveryRunsResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ListDiscoveryRunsResponse.ProtoReflect.Descriptor instead. -func (*ListDiscoveryRunsResponse) Descriptor() ([]byte, []int) { - return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{29} +// Deprecated: Use ListImportRunsResponse.ProtoReflect.Descriptor instead. +func (*ListImportRunsResponse) Descriptor() ([]byte, []int) { + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{39} } -func (x *ListDiscoveryRunsResponse) GetDiscoveryRuns() []*DiscoveryRun { +func (x *ListImportRunsResponse) GetImportRuns() []*ImportRun { if x != nil { - return x.DiscoveryRuns + return x.ImportRuns } return nil } @@ -1859,7 +2496,7 @@ type ListPatternsRequest struct { func (x *ListPatternsRequest) Reset() { *x = ListPatternsRequest{} - mi := &file_lapp_web_v1_web_proto_msgTypes[30] + mi := &file_lapp_web_v1_web_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1871,7 +2508,7 @@ func (x *ListPatternsRequest) String() string { func (*ListPatternsRequest) ProtoMessage() {} func (x *ListPatternsRequest) ProtoReflect() protoreflect.Message { - mi := &file_lapp_web_v1_web_proto_msgTypes[30] + mi := &file_lapp_web_v1_web_proto_msgTypes[40] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1884,7 +2521,7 @@ func (x *ListPatternsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListPatternsRequest.ProtoReflect.Descriptor instead. func (*ListPatternsRequest) Descriptor() ([]byte, []int) { - return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{30} + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{40} } func (x *ListPatternsRequest) GetParent() string { @@ -1903,7 +2540,7 @@ type ListPatternsResponse struct { func (x *ListPatternsResponse) Reset() { *x = ListPatternsResponse{} - mi := &file_lapp_web_v1_web_proto_msgTypes[31] + mi := &file_lapp_web_v1_web_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1915,7 +2552,7 @@ func (x *ListPatternsResponse) String() string { func (*ListPatternsResponse) ProtoMessage() {} func (x *ListPatternsResponse) ProtoReflect() protoreflect.Message { - mi := &file_lapp_web_v1_web_proto_msgTypes[31] + mi := &file_lapp_web_v1_web_proto_msgTypes[41] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1928,7 +2565,7 @@ func (x *ListPatternsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListPatternsResponse.ProtoReflect.Descriptor instead. func (*ListPatternsResponse) Descriptor() ([]byte, []int) { - return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{31} + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{41} } func (x *ListPatternsResponse) GetPatterns() []*Pattern { @@ -1947,7 +2584,7 @@ type GetPatternRequest struct { func (x *GetPatternRequest) Reset() { *x = GetPatternRequest{} - mi := &file_lapp_web_v1_web_proto_msgTypes[32] + mi := &file_lapp_web_v1_web_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1959,7 +2596,7 @@ func (x *GetPatternRequest) String() string { func (*GetPatternRequest) ProtoMessage() {} func (x *GetPatternRequest) ProtoReflect() protoreflect.Message { - mi := &file_lapp_web_v1_web_proto_msgTypes[32] + mi := &file_lapp_web_v1_web_proto_msgTypes[42] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1972,7 +2609,7 @@ func (x *GetPatternRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetPatternRequest.ProtoReflect.Descriptor instead. func (*GetPatternRequest) Descriptor() ([]byte, []int) { - return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{32} + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{42} } func (x *GetPatternRequest) GetName() string { @@ -1991,7 +2628,7 @@ type GetPatternResponse struct { func (x *GetPatternResponse) Reset() { *x = GetPatternResponse{} - mi := &file_lapp_web_v1_web_proto_msgTypes[33] + mi := &file_lapp_web_v1_web_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2003,7 +2640,7 @@ func (x *GetPatternResponse) String() string { func (*GetPatternResponse) ProtoMessage() {} func (x *GetPatternResponse) ProtoReflect() protoreflect.Message { - mi := &file_lapp_web_v1_web_proto_msgTypes[33] + mi := &file_lapp_web_v1_web_proto_msgTypes[43] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2016,7 +2653,7 @@ func (x *GetPatternResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetPatternResponse.ProtoReflect.Descriptor instead. func (*GetPatternResponse) Descriptor() ([]byte, []int) { - return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{33} + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{43} } func (x *GetPatternResponse) GetPattern() *Pattern { @@ -2035,7 +2672,7 @@ type GetErrorsViewRequest struct { func (x *GetErrorsViewRequest) Reset() { *x = GetErrorsViewRequest{} - mi := &file_lapp_web_v1_web_proto_msgTypes[34] + mi := &file_lapp_web_v1_web_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2047,7 +2684,7 @@ func (x *GetErrorsViewRequest) String() string { func (*GetErrorsViewRequest) ProtoMessage() {} func (x *GetErrorsViewRequest) ProtoReflect() protoreflect.Message { - mi := &file_lapp_web_v1_web_proto_msgTypes[34] + mi := &file_lapp_web_v1_web_proto_msgTypes[44] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2060,7 +2697,7 @@ func (x *GetErrorsViewRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetErrorsViewRequest.ProtoReflect.Descriptor instead. func (*GetErrorsViewRequest) Descriptor() ([]byte, []int) { - return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{34} + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{44} } func (x *GetErrorsViewRequest) GetParent() string { @@ -2080,7 +2717,7 @@ type GetErrorsViewResponse struct { func (x *GetErrorsViewResponse) Reset() { *x = GetErrorsViewResponse{} - mi := &file_lapp_web_v1_web_proto_msgTypes[35] + mi := &file_lapp_web_v1_web_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2092,7 +2729,7 @@ func (x *GetErrorsViewResponse) String() string { func (*GetErrorsViewResponse) ProtoMessage() {} func (x *GetErrorsViewResponse) ProtoReflect() protoreflect.Message { - mi := &file_lapp_web_v1_web_proto_msgTypes[35] + mi := &file_lapp_web_v1_web_proto_msgTypes[45] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2105,7 +2742,7 @@ func (x *GetErrorsViewResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetErrorsViewResponse.ProtoReflect.Descriptor instead. func (*GetErrorsViewResponse) Descriptor() ([]byte, []int) { - return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{35} + return file_lapp_web_v1_web_proto_rawDescGZIP(), []int{45} } func (x *GetErrorsViewResponse) GetErrorPatterns() []*ErrorPattern { @@ -2176,7 +2813,35 @@ const file_lapp_web_v1_web_proto_rawDesc = "" + "\x11DiscoveryRunError\x12\x12\n" + "\x04code\x18\x01 \x01(\tR\x04code\x12\x18\n" + "\amessage\x18\x02 \x01(\tR\amessage\x12.\n" + - "\x04step\x18\x03 \x01(\x0e2\x1a.lapp.web.v1.DiscoveryStepR\x04step\"\xe6\x02\n" + + "\x04step\x18\x03 \x01(\x0e2\x1a.lapp.web.v1.DiscoveryStepR\x04step\"\xba\x03\n" + + "\tImportRun\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12\"\n" + + "\rimport_run_id\x18\x02 \x01(\tR\vimportRunId\x121\n" + + "\x05state\x18\x03 \x01(\x0e2\x1b.lapp.web.v1.ImportRunStateR\x05state\x12\x16\n" + + "\x06source\x18\x04 \x01(\tR\x06source\x129\n" + + "\n" + + "started_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\tstartedAt\x12;\n" + + "\vfinished_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\n" + + "finishedAt\x12\x1f\n" + + "\ventry_count\x18\a \x01(\x05R\n" + + "entryCount\x12\"\n" + + "\rlog_file_name\x18\b \x01(\tR\vlogFileName\x12:\n" + + "\bprogress\x18\t \x01(\v2\x1e.lapp.web.v1.ImportRunProgressR\bprogress\x121\n" + + "\x05error\x18\n" + + " \x01(\v2\x1b.lapp.web.v1.ImportRunErrorR\x05error\"8\n" + + "\x11ImportRunProgress\x12#\n" + + "\rfetched_count\x18\x01 \x01(\x05R\ffetchedCount\">\n" + + "\x0eImportRunError\x12\x12\n" + + "\x04code\x18\x01 \x01(\tR\x04code\x12\x18\n" + + "\amessage\x18\x02 \x01(\tR\amessage\"\xd1\x01\n" + + "\x10GcpLoggingSource\x12\x1d\n" + + "\n" + + "project_id\x18\x01 \x01(\tR\tprojectId\x12\x16\n" + + "\x06filter\x18\x02 \x01(\tR\x06filter\x129\n" + + "\n" + + "start_time\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\tstartTime\x125\n" + + "\bend_time\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\aendTime\x12\x14\n" + + "\x05limit\x18\x05 \x01(\x05R\x05limit\"\xe6\x02\n" + "\aPattern\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x1d\n" + "\n" + @@ -2244,7 +2909,25 @@ const file_lapp_web_v1_web_proto_rawDesc = "" + "\x18ListDiscoveryRunsRequest\x12\x16\n" + "\x06parent\x18\x01 \x01(\tR\x06parent\"]\n" + "\x19ListDiscoveryRunsResponse\x12@\n" + - "\x0ediscovery_runs\x18\x01 \x03(\v2\x19.lapp.web.v1.DiscoveryRunR\rdiscoveryRuns\"-\n" + + "\x0ediscovery_runs\x18\x01 \x03(\v2\x19.lapp.web.v1.DiscoveryRunR\rdiscoveryRuns\"w\n" + + "\x11ImportLogsRequest\x12\x16\n" + + "\x06parent\x18\x01 \x01(\tR\x06parent\x12@\n" + + "\vgcp_logging\x18\x02 \x01(\v2\x1d.lapp.web.v1.GcpLoggingSourceH\x00R\n" + + "gcpLoggingB\b\n" + + "\x06source\"K\n" + + "\x12ImportLogsResponse\x125\n" + + "\n" + + "import_run\x18\x01 \x01(\v2\x16.lapp.web.v1.ImportRunR\timportRun\")\n" + + "\x13GetImportRunRequest\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\"M\n" + + "\x14GetImportRunResponse\x125\n" + + "\n" + + "import_run\x18\x01 \x01(\v2\x16.lapp.web.v1.ImportRunR\timportRun\"/\n" + + "\x15ListImportRunsRequest\x12\x16\n" + + "\x06parent\x18\x01 \x01(\tR\x06parent\"Q\n" + + "\x16ListImportRunsResponse\x127\n" + + "\vimport_runs\x18\x01 \x03(\v2\x16.lapp.web.v1.ImportRunR\n" + + "importRuns\"-\n" + "\x13ListPatternsRequest\x12\x16\n" + "\x06parent\x18\x01 \x01(\tR\x06parent\"H\n" + "\x14ListPatternsResponse\x120\n" + @@ -2276,7 +2959,13 @@ const file_lapp_web_v1_web_proto_rawDesc = "" + "\x1eDISCOVERY_STEP_MERGING_ENTRIES\x10\x02\x12'\n" + "#DISCOVERY_STEP_DISCOVERING_PATTERNS\x10\x03\x12$\n" + " DISCOVERY_STEP_LABELING_PATTERNS\x10\x04\x12\"\n" + - "\x1eDISCOVERY_STEP_WRITING_RESULTS\x10\x052\xa8\t\n" + + "\x1eDISCOVERY_STEP_WRITING_RESULTS\x10\x05*\xaa\x01\n" + + "\x0eImportRunState\x12 \n" + + "\x1cIMPORT_RUN_STATE_UNSPECIFIED\x10\x00\x12\x1b\n" + + "\x17IMPORT_RUN_STATE_QUEUED\x10\x01\x12\x1c\n" + + "\x18IMPORT_RUN_STATE_RUNNING\x10\x02\x12\x1e\n" + + "\x1aIMPORT_RUN_STATE_SUCCEEDED\x10\x03\x12\x1b\n" + + "\x17IMPORT_RUN_STATE_FAILED\x10\x042\xa7\v\n" + "\x10WorkspaceService\x12Y\n" + "\x0eListWorkspaces\x12\".lapp.web.v1.ListWorkspacesRequest\x1a#.lapp.web.v1.ListWorkspacesResponse\x12S\n" + "\fGetWorkspace\x12 .lapp.web.v1.GetWorkspaceRequest\x1a!.lapp.web.v1.GetWorkspaceResponse\x12\\\n" + @@ -2287,7 +2976,11 @@ const file_lapp_web_v1_web_proto_rawDesc = "" + "\rDeleteLogFile\x12!.lapp.web.v1.DeleteLogFileRequest\x1a\".lapp.web.v1.DeleteLogFileResponse\x12e\n" + "\x12CreateDiscoveryRun\x12&.lapp.web.v1.CreateDiscoveryRunRequest\x1a'.lapp.web.v1.CreateDiscoveryRunResponse\x12\\\n" + "\x0fGetDiscoveryRun\x12#.lapp.web.v1.GetDiscoveryRunRequest\x1a$.lapp.web.v1.GetDiscoveryRunResponse\x12b\n" + - "\x11ListDiscoveryRuns\x12%.lapp.web.v1.ListDiscoveryRunsRequest\x1a&.lapp.web.v1.ListDiscoveryRunsResponse\x12S\n" + + "\x11ListDiscoveryRuns\x12%.lapp.web.v1.ListDiscoveryRunsRequest\x1a&.lapp.web.v1.ListDiscoveryRunsResponse\x12M\n" + + "\n" + + "ImportLogs\x12\x1e.lapp.web.v1.ImportLogsRequest\x1a\x1f.lapp.web.v1.ImportLogsResponse\x12S\n" + + "\fGetImportRun\x12 .lapp.web.v1.GetImportRunRequest\x1a!.lapp.web.v1.GetImportRunResponse\x12Y\n" + + "\x0eListImportRuns\x12\".lapp.web.v1.ListImportRunsRequest\x1a#.lapp.web.v1.ListImportRunsResponse\x12S\n" + "\fListPatterns\x12 .lapp.web.v1.ListPatternsRequest\x1a!.lapp.web.v1.ListPatternsResponse\x12M\n" + "\n" + "GetPattern\x12\x1e.lapp.web.v1.GetPatternRequest\x1a\x1f.lapp.web.v1.GetPatternResponse\x12V\n" + @@ -2305,111 +2998,139 @@ func file_lapp_web_v1_web_proto_rawDescGZIP() []byte { return file_lapp_web_v1_web_proto_rawDescData } -var file_lapp_web_v1_web_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_lapp_web_v1_web_proto_msgTypes = make([]protoimpl.MessageInfo, 36) +var file_lapp_web_v1_web_proto_enumTypes = make([]protoimpl.EnumInfo, 4) +var file_lapp_web_v1_web_proto_msgTypes = make([]protoimpl.MessageInfo, 46) var file_lapp_web_v1_web_proto_goTypes = []any{ (WorkspaceStatus)(0), // 0: lapp.web.v1.WorkspaceStatus (DiscoveryRunState)(0), // 1: lapp.web.v1.DiscoveryRunState (DiscoveryStep)(0), // 2: lapp.web.v1.DiscoveryStep - (*Workspace)(nil), // 3: lapp.web.v1.Workspace - (*LogFile)(nil), // 4: lapp.web.v1.LogFile - (*DiscoveryRun)(nil), // 5: lapp.web.v1.DiscoveryRun - (*DiscoveryRunProgress)(nil), // 6: lapp.web.v1.DiscoveryRunProgress - (*DiscoveryLabelBatchProgress)(nil), // 7: lapp.web.v1.DiscoveryLabelBatchProgress - (*DiscoveryRunError)(nil), // 8: lapp.web.v1.DiscoveryRunError - (*Pattern)(nil), // 9: lapp.web.v1.Pattern - (*LineRef)(nil), // 10: lapp.web.v1.LineRef - (*ErrorPattern)(nil), // 11: lapp.web.v1.ErrorPattern - (*UnmatchedErrorLine)(nil), // 12: lapp.web.v1.UnmatchedErrorLine - (*ListWorkspacesRequest)(nil), // 13: lapp.web.v1.ListWorkspacesRequest - (*ListWorkspacesResponse)(nil), // 14: lapp.web.v1.ListWorkspacesResponse - (*GetWorkspaceRequest)(nil), // 15: lapp.web.v1.GetWorkspaceRequest - (*GetWorkspaceResponse)(nil), // 16: lapp.web.v1.GetWorkspaceResponse - (*CreateWorkspaceRequest)(nil), // 17: lapp.web.v1.CreateWorkspaceRequest - (*CreateWorkspaceResponse)(nil), // 18: lapp.web.v1.CreateWorkspaceResponse - (*DeleteWorkspaceRequest)(nil), // 19: lapp.web.v1.DeleteWorkspaceRequest - (*DeleteWorkspaceResponse)(nil), // 20: lapp.web.v1.DeleteWorkspaceResponse - (*ListLogFilesRequest)(nil), // 21: lapp.web.v1.ListLogFilesRequest - (*ListLogFilesResponse)(nil), // 22: lapp.web.v1.ListLogFilesResponse - (*UploadLogFileRequest)(nil), // 23: lapp.web.v1.UploadLogFileRequest - (*UploadLogFileResponse)(nil), // 24: lapp.web.v1.UploadLogFileResponse - (*DeleteLogFileRequest)(nil), // 25: lapp.web.v1.DeleteLogFileRequest - (*DeleteLogFileResponse)(nil), // 26: lapp.web.v1.DeleteLogFileResponse - (*CreateDiscoveryRunRequest)(nil), // 27: lapp.web.v1.CreateDiscoveryRunRequest - (*CreateDiscoveryRunResponse)(nil), // 28: lapp.web.v1.CreateDiscoveryRunResponse - (*GetDiscoveryRunRequest)(nil), // 29: lapp.web.v1.GetDiscoveryRunRequest - (*GetDiscoveryRunResponse)(nil), // 30: lapp.web.v1.GetDiscoveryRunResponse - (*ListDiscoveryRunsRequest)(nil), // 31: lapp.web.v1.ListDiscoveryRunsRequest - (*ListDiscoveryRunsResponse)(nil), // 32: lapp.web.v1.ListDiscoveryRunsResponse - (*ListPatternsRequest)(nil), // 33: lapp.web.v1.ListPatternsRequest - (*ListPatternsResponse)(nil), // 34: lapp.web.v1.ListPatternsResponse - (*GetPatternRequest)(nil), // 35: lapp.web.v1.GetPatternRequest - (*GetPatternResponse)(nil), // 36: lapp.web.v1.GetPatternResponse - (*GetErrorsViewRequest)(nil), // 37: lapp.web.v1.GetErrorsViewRequest - (*GetErrorsViewResponse)(nil), // 38: lapp.web.v1.GetErrorsViewResponse - (*timestamppb.Timestamp)(nil), // 39: google.protobuf.Timestamp + (ImportRunState)(0), // 3: lapp.web.v1.ImportRunState + (*Workspace)(nil), // 4: lapp.web.v1.Workspace + (*LogFile)(nil), // 5: lapp.web.v1.LogFile + (*DiscoveryRun)(nil), // 6: lapp.web.v1.DiscoveryRun + (*DiscoveryRunProgress)(nil), // 7: lapp.web.v1.DiscoveryRunProgress + (*DiscoveryLabelBatchProgress)(nil), // 8: lapp.web.v1.DiscoveryLabelBatchProgress + (*DiscoveryRunError)(nil), // 9: lapp.web.v1.DiscoveryRunError + (*ImportRun)(nil), // 10: lapp.web.v1.ImportRun + (*ImportRunProgress)(nil), // 11: lapp.web.v1.ImportRunProgress + (*ImportRunError)(nil), // 12: lapp.web.v1.ImportRunError + (*GcpLoggingSource)(nil), // 13: lapp.web.v1.GcpLoggingSource + (*Pattern)(nil), // 14: lapp.web.v1.Pattern + (*LineRef)(nil), // 15: lapp.web.v1.LineRef + (*ErrorPattern)(nil), // 16: lapp.web.v1.ErrorPattern + (*UnmatchedErrorLine)(nil), // 17: lapp.web.v1.UnmatchedErrorLine + (*ListWorkspacesRequest)(nil), // 18: lapp.web.v1.ListWorkspacesRequest + (*ListWorkspacesResponse)(nil), // 19: lapp.web.v1.ListWorkspacesResponse + (*GetWorkspaceRequest)(nil), // 20: lapp.web.v1.GetWorkspaceRequest + (*GetWorkspaceResponse)(nil), // 21: lapp.web.v1.GetWorkspaceResponse + (*CreateWorkspaceRequest)(nil), // 22: lapp.web.v1.CreateWorkspaceRequest + (*CreateWorkspaceResponse)(nil), // 23: lapp.web.v1.CreateWorkspaceResponse + (*DeleteWorkspaceRequest)(nil), // 24: lapp.web.v1.DeleteWorkspaceRequest + (*DeleteWorkspaceResponse)(nil), // 25: lapp.web.v1.DeleteWorkspaceResponse + (*ListLogFilesRequest)(nil), // 26: lapp.web.v1.ListLogFilesRequest + (*ListLogFilesResponse)(nil), // 27: lapp.web.v1.ListLogFilesResponse + (*UploadLogFileRequest)(nil), // 28: lapp.web.v1.UploadLogFileRequest + (*UploadLogFileResponse)(nil), // 29: lapp.web.v1.UploadLogFileResponse + (*DeleteLogFileRequest)(nil), // 30: lapp.web.v1.DeleteLogFileRequest + (*DeleteLogFileResponse)(nil), // 31: lapp.web.v1.DeleteLogFileResponse + (*CreateDiscoveryRunRequest)(nil), // 32: lapp.web.v1.CreateDiscoveryRunRequest + (*CreateDiscoveryRunResponse)(nil), // 33: lapp.web.v1.CreateDiscoveryRunResponse + (*GetDiscoveryRunRequest)(nil), // 34: lapp.web.v1.GetDiscoveryRunRequest + (*GetDiscoveryRunResponse)(nil), // 35: lapp.web.v1.GetDiscoveryRunResponse + (*ListDiscoveryRunsRequest)(nil), // 36: lapp.web.v1.ListDiscoveryRunsRequest + (*ListDiscoveryRunsResponse)(nil), // 37: lapp.web.v1.ListDiscoveryRunsResponse + (*ImportLogsRequest)(nil), // 38: lapp.web.v1.ImportLogsRequest + (*ImportLogsResponse)(nil), // 39: lapp.web.v1.ImportLogsResponse + (*GetImportRunRequest)(nil), // 40: lapp.web.v1.GetImportRunRequest + (*GetImportRunResponse)(nil), // 41: lapp.web.v1.GetImportRunResponse + (*ListImportRunsRequest)(nil), // 42: lapp.web.v1.ListImportRunsRequest + (*ListImportRunsResponse)(nil), // 43: lapp.web.v1.ListImportRunsResponse + (*ListPatternsRequest)(nil), // 44: lapp.web.v1.ListPatternsRequest + (*ListPatternsResponse)(nil), // 45: lapp.web.v1.ListPatternsResponse + (*GetPatternRequest)(nil), // 46: lapp.web.v1.GetPatternRequest + (*GetPatternResponse)(nil), // 47: lapp.web.v1.GetPatternResponse + (*GetErrorsViewRequest)(nil), // 48: lapp.web.v1.GetErrorsViewRequest + (*GetErrorsViewResponse)(nil), // 49: lapp.web.v1.GetErrorsViewResponse + (*timestamppb.Timestamp)(nil), // 50: google.protobuf.Timestamp } var file_lapp_web_v1_web_proto_depIdxs = []int32{ 0, // 0: lapp.web.v1.Workspace.status:type_name -> lapp.web.v1.WorkspaceStatus - 5, // 1: lapp.web.v1.Workspace.latest_discovery_run:type_name -> lapp.web.v1.DiscoveryRun - 5, // 2: lapp.web.v1.Workspace.latest_successful_discovery_run:type_name -> lapp.web.v1.DiscoveryRun - 39, // 3: lapp.web.v1.LogFile.updated_at:type_name -> google.protobuf.Timestamp + 6, // 1: lapp.web.v1.Workspace.latest_discovery_run:type_name -> lapp.web.v1.DiscoveryRun + 6, // 2: lapp.web.v1.Workspace.latest_successful_discovery_run:type_name -> lapp.web.v1.DiscoveryRun + 50, // 3: lapp.web.v1.LogFile.updated_at:type_name -> google.protobuf.Timestamp 1, // 4: lapp.web.v1.DiscoveryRun.state:type_name -> lapp.web.v1.DiscoveryRunState 2, // 5: lapp.web.v1.DiscoveryRun.current_step:type_name -> lapp.web.v1.DiscoveryStep - 39, // 6: lapp.web.v1.DiscoveryRun.started_at:type_name -> google.protobuf.Timestamp - 39, // 7: lapp.web.v1.DiscoveryRun.finished_at:type_name -> google.protobuf.Timestamp - 6, // 8: lapp.web.v1.DiscoveryRun.progress:type_name -> lapp.web.v1.DiscoveryRunProgress - 8, // 9: lapp.web.v1.DiscoveryRun.error:type_name -> lapp.web.v1.DiscoveryRunError + 50, // 6: lapp.web.v1.DiscoveryRun.started_at:type_name -> google.protobuf.Timestamp + 50, // 7: lapp.web.v1.DiscoveryRun.finished_at:type_name -> google.protobuf.Timestamp + 7, // 8: lapp.web.v1.DiscoveryRun.progress:type_name -> lapp.web.v1.DiscoveryRunProgress + 9, // 9: lapp.web.v1.DiscoveryRun.error:type_name -> lapp.web.v1.DiscoveryRunError 2, // 10: lapp.web.v1.DiscoveryRunProgress.step:type_name -> lapp.web.v1.DiscoveryStep - 7, // 11: lapp.web.v1.DiscoveryRunProgress.label_batch:type_name -> lapp.web.v1.DiscoveryLabelBatchProgress + 8, // 11: lapp.web.v1.DiscoveryRunProgress.label_batch:type_name -> lapp.web.v1.DiscoveryLabelBatchProgress 2, // 12: lapp.web.v1.DiscoveryRunError.step:type_name -> lapp.web.v1.DiscoveryStep - 10, // 13: lapp.web.v1.Pattern.first_seen:type_name -> lapp.web.v1.LineRef - 10, // 14: lapp.web.v1.Pattern.last_seen:type_name -> lapp.web.v1.LineRef - 10, // 15: lapp.web.v1.Pattern.line_refs:type_name -> lapp.web.v1.LineRef - 9, // 16: lapp.web.v1.ErrorPattern.pattern:type_name -> lapp.web.v1.Pattern - 3, // 17: lapp.web.v1.ListWorkspacesResponse.workspaces:type_name -> lapp.web.v1.Workspace - 3, // 18: lapp.web.v1.GetWorkspaceResponse.workspace:type_name -> lapp.web.v1.Workspace - 3, // 19: lapp.web.v1.CreateWorkspaceResponse.workspace:type_name -> lapp.web.v1.Workspace - 4, // 20: lapp.web.v1.ListLogFilesResponse.log_files:type_name -> lapp.web.v1.LogFile - 4, // 21: lapp.web.v1.UploadLogFileResponse.log_file:type_name -> lapp.web.v1.LogFile - 5, // 22: lapp.web.v1.CreateDiscoveryRunResponse.discovery_run:type_name -> lapp.web.v1.DiscoveryRun - 5, // 23: lapp.web.v1.GetDiscoveryRunResponse.discovery_run:type_name -> lapp.web.v1.DiscoveryRun - 5, // 24: lapp.web.v1.ListDiscoveryRunsResponse.discovery_runs:type_name -> lapp.web.v1.DiscoveryRun - 9, // 25: lapp.web.v1.ListPatternsResponse.patterns:type_name -> lapp.web.v1.Pattern - 9, // 26: lapp.web.v1.GetPatternResponse.pattern:type_name -> lapp.web.v1.Pattern - 11, // 27: lapp.web.v1.GetErrorsViewResponse.error_patterns:type_name -> lapp.web.v1.ErrorPattern - 12, // 28: lapp.web.v1.GetErrorsViewResponse.unmatched_error_lines:type_name -> lapp.web.v1.UnmatchedErrorLine - 13, // 29: lapp.web.v1.WorkspaceService.ListWorkspaces:input_type -> lapp.web.v1.ListWorkspacesRequest - 15, // 30: lapp.web.v1.WorkspaceService.GetWorkspace:input_type -> lapp.web.v1.GetWorkspaceRequest - 17, // 31: lapp.web.v1.WorkspaceService.CreateWorkspace:input_type -> lapp.web.v1.CreateWorkspaceRequest - 19, // 32: lapp.web.v1.WorkspaceService.DeleteWorkspace:input_type -> lapp.web.v1.DeleteWorkspaceRequest - 21, // 33: lapp.web.v1.WorkspaceService.ListLogFiles:input_type -> lapp.web.v1.ListLogFilesRequest - 23, // 34: lapp.web.v1.WorkspaceService.UploadLogFile:input_type -> lapp.web.v1.UploadLogFileRequest - 25, // 35: lapp.web.v1.WorkspaceService.DeleteLogFile:input_type -> lapp.web.v1.DeleteLogFileRequest - 27, // 36: lapp.web.v1.WorkspaceService.CreateDiscoveryRun:input_type -> lapp.web.v1.CreateDiscoveryRunRequest - 29, // 37: lapp.web.v1.WorkspaceService.GetDiscoveryRun:input_type -> lapp.web.v1.GetDiscoveryRunRequest - 31, // 38: lapp.web.v1.WorkspaceService.ListDiscoveryRuns:input_type -> lapp.web.v1.ListDiscoveryRunsRequest - 33, // 39: lapp.web.v1.WorkspaceService.ListPatterns:input_type -> lapp.web.v1.ListPatternsRequest - 35, // 40: lapp.web.v1.WorkspaceService.GetPattern:input_type -> lapp.web.v1.GetPatternRequest - 37, // 41: lapp.web.v1.WorkspaceService.GetErrorsView:input_type -> lapp.web.v1.GetErrorsViewRequest - 14, // 42: lapp.web.v1.WorkspaceService.ListWorkspaces:output_type -> lapp.web.v1.ListWorkspacesResponse - 16, // 43: lapp.web.v1.WorkspaceService.GetWorkspace:output_type -> lapp.web.v1.GetWorkspaceResponse - 18, // 44: lapp.web.v1.WorkspaceService.CreateWorkspace:output_type -> lapp.web.v1.CreateWorkspaceResponse - 20, // 45: lapp.web.v1.WorkspaceService.DeleteWorkspace:output_type -> lapp.web.v1.DeleteWorkspaceResponse - 22, // 46: lapp.web.v1.WorkspaceService.ListLogFiles:output_type -> lapp.web.v1.ListLogFilesResponse - 24, // 47: lapp.web.v1.WorkspaceService.UploadLogFile:output_type -> lapp.web.v1.UploadLogFileResponse - 26, // 48: lapp.web.v1.WorkspaceService.DeleteLogFile:output_type -> lapp.web.v1.DeleteLogFileResponse - 28, // 49: lapp.web.v1.WorkspaceService.CreateDiscoveryRun:output_type -> lapp.web.v1.CreateDiscoveryRunResponse - 30, // 50: lapp.web.v1.WorkspaceService.GetDiscoveryRun:output_type -> lapp.web.v1.GetDiscoveryRunResponse - 32, // 51: lapp.web.v1.WorkspaceService.ListDiscoveryRuns:output_type -> lapp.web.v1.ListDiscoveryRunsResponse - 34, // 52: lapp.web.v1.WorkspaceService.ListPatterns:output_type -> lapp.web.v1.ListPatternsResponse - 36, // 53: lapp.web.v1.WorkspaceService.GetPattern:output_type -> lapp.web.v1.GetPatternResponse - 38, // 54: lapp.web.v1.WorkspaceService.GetErrorsView:output_type -> lapp.web.v1.GetErrorsViewResponse - 42, // [42:55] is the sub-list for method output_type - 29, // [29:42] is the sub-list for method input_type - 29, // [29:29] is the sub-list for extension type_name - 29, // [29:29] is the sub-list for extension extendee - 0, // [0:29] is the sub-list for field type_name + 3, // 13: lapp.web.v1.ImportRun.state:type_name -> lapp.web.v1.ImportRunState + 50, // 14: lapp.web.v1.ImportRun.started_at:type_name -> google.protobuf.Timestamp + 50, // 15: lapp.web.v1.ImportRun.finished_at:type_name -> google.protobuf.Timestamp + 11, // 16: lapp.web.v1.ImportRun.progress:type_name -> lapp.web.v1.ImportRunProgress + 12, // 17: lapp.web.v1.ImportRun.error:type_name -> lapp.web.v1.ImportRunError + 50, // 18: lapp.web.v1.GcpLoggingSource.start_time:type_name -> google.protobuf.Timestamp + 50, // 19: lapp.web.v1.GcpLoggingSource.end_time:type_name -> google.protobuf.Timestamp + 15, // 20: lapp.web.v1.Pattern.first_seen:type_name -> lapp.web.v1.LineRef + 15, // 21: lapp.web.v1.Pattern.last_seen:type_name -> lapp.web.v1.LineRef + 15, // 22: lapp.web.v1.Pattern.line_refs:type_name -> lapp.web.v1.LineRef + 14, // 23: lapp.web.v1.ErrorPattern.pattern:type_name -> lapp.web.v1.Pattern + 4, // 24: lapp.web.v1.ListWorkspacesResponse.workspaces:type_name -> lapp.web.v1.Workspace + 4, // 25: lapp.web.v1.GetWorkspaceResponse.workspace:type_name -> lapp.web.v1.Workspace + 4, // 26: lapp.web.v1.CreateWorkspaceResponse.workspace:type_name -> lapp.web.v1.Workspace + 5, // 27: lapp.web.v1.ListLogFilesResponse.log_files:type_name -> lapp.web.v1.LogFile + 5, // 28: lapp.web.v1.UploadLogFileResponse.log_file:type_name -> lapp.web.v1.LogFile + 6, // 29: lapp.web.v1.CreateDiscoveryRunResponse.discovery_run:type_name -> lapp.web.v1.DiscoveryRun + 6, // 30: lapp.web.v1.GetDiscoveryRunResponse.discovery_run:type_name -> lapp.web.v1.DiscoveryRun + 6, // 31: lapp.web.v1.ListDiscoveryRunsResponse.discovery_runs:type_name -> lapp.web.v1.DiscoveryRun + 13, // 32: lapp.web.v1.ImportLogsRequest.gcp_logging:type_name -> lapp.web.v1.GcpLoggingSource + 10, // 33: lapp.web.v1.ImportLogsResponse.import_run:type_name -> lapp.web.v1.ImportRun + 10, // 34: lapp.web.v1.GetImportRunResponse.import_run:type_name -> lapp.web.v1.ImportRun + 10, // 35: lapp.web.v1.ListImportRunsResponse.import_runs:type_name -> lapp.web.v1.ImportRun + 14, // 36: lapp.web.v1.ListPatternsResponse.patterns:type_name -> lapp.web.v1.Pattern + 14, // 37: lapp.web.v1.GetPatternResponse.pattern:type_name -> lapp.web.v1.Pattern + 16, // 38: lapp.web.v1.GetErrorsViewResponse.error_patterns:type_name -> lapp.web.v1.ErrorPattern + 17, // 39: lapp.web.v1.GetErrorsViewResponse.unmatched_error_lines:type_name -> lapp.web.v1.UnmatchedErrorLine + 18, // 40: lapp.web.v1.WorkspaceService.ListWorkspaces:input_type -> lapp.web.v1.ListWorkspacesRequest + 20, // 41: lapp.web.v1.WorkspaceService.GetWorkspace:input_type -> lapp.web.v1.GetWorkspaceRequest + 22, // 42: lapp.web.v1.WorkspaceService.CreateWorkspace:input_type -> lapp.web.v1.CreateWorkspaceRequest + 24, // 43: lapp.web.v1.WorkspaceService.DeleteWorkspace:input_type -> lapp.web.v1.DeleteWorkspaceRequest + 26, // 44: lapp.web.v1.WorkspaceService.ListLogFiles:input_type -> lapp.web.v1.ListLogFilesRequest + 28, // 45: lapp.web.v1.WorkspaceService.UploadLogFile:input_type -> lapp.web.v1.UploadLogFileRequest + 30, // 46: lapp.web.v1.WorkspaceService.DeleteLogFile:input_type -> lapp.web.v1.DeleteLogFileRequest + 32, // 47: lapp.web.v1.WorkspaceService.CreateDiscoveryRun:input_type -> lapp.web.v1.CreateDiscoveryRunRequest + 34, // 48: lapp.web.v1.WorkspaceService.GetDiscoveryRun:input_type -> lapp.web.v1.GetDiscoveryRunRequest + 36, // 49: lapp.web.v1.WorkspaceService.ListDiscoveryRuns:input_type -> lapp.web.v1.ListDiscoveryRunsRequest + 38, // 50: lapp.web.v1.WorkspaceService.ImportLogs:input_type -> lapp.web.v1.ImportLogsRequest + 40, // 51: lapp.web.v1.WorkspaceService.GetImportRun:input_type -> lapp.web.v1.GetImportRunRequest + 42, // 52: lapp.web.v1.WorkspaceService.ListImportRuns:input_type -> lapp.web.v1.ListImportRunsRequest + 44, // 53: lapp.web.v1.WorkspaceService.ListPatterns:input_type -> lapp.web.v1.ListPatternsRequest + 46, // 54: lapp.web.v1.WorkspaceService.GetPattern:input_type -> lapp.web.v1.GetPatternRequest + 48, // 55: lapp.web.v1.WorkspaceService.GetErrorsView:input_type -> lapp.web.v1.GetErrorsViewRequest + 19, // 56: lapp.web.v1.WorkspaceService.ListWorkspaces:output_type -> lapp.web.v1.ListWorkspacesResponse + 21, // 57: lapp.web.v1.WorkspaceService.GetWorkspace:output_type -> lapp.web.v1.GetWorkspaceResponse + 23, // 58: lapp.web.v1.WorkspaceService.CreateWorkspace:output_type -> lapp.web.v1.CreateWorkspaceResponse + 25, // 59: lapp.web.v1.WorkspaceService.DeleteWorkspace:output_type -> lapp.web.v1.DeleteWorkspaceResponse + 27, // 60: lapp.web.v1.WorkspaceService.ListLogFiles:output_type -> lapp.web.v1.ListLogFilesResponse + 29, // 61: lapp.web.v1.WorkspaceService.UploadLogFile:output_type -> lapp.web.v1.UploadLogFileResponse + 31, // 62: lapp.web.v1.WorkspaceService.DeleteLogFile:output_type -> lapp.web.v1.DeleteLogFileResponse + 33, // 63: lapp.web.v1.WorkspaceService.CreateDiscoveryRun:output_type -> lapp.web.v1.CreateDiscoveryRunResponse + 35, // 64: lapp.web.v1.WorkspaceService.GetDiscoveryRun:output_type -> lapp.web.v1.GetDiscoveryRunResponse + 37, // 65: lapp.web.v1.WorkspaceService.ListDiscoveryRuns:output_type -> lapp.web.v1.ListDiscoveryRunsResponse + 39, // 66: lapp.web.v1.WorkspaceService.ImportLogs:output_type -> lapp.web.v1.ImportLogsResponse + 41, // 67: lapp.web.v1.WorkspaceService.GetImportRun:output_type -> lapp.web.v1.GetImportRunResponse + 43, // 68: lapp.web.v1.WorkspaceService.ListImportRuns:output_type -> lapp.web.v1.ListImportRunsResponse + 45, // 69: lapp.web.v1.WorkspaceService.ListPatterns:output_type -> lapp.web.v1.ListPatternsResponse + 47, // 70: lapp.web.v1.WorkspaceService.GetPattern:output_type -> lapp.web.v1.GetPatternResponse + 49, // 71: lapp.web.v1.WorkspaceService.GetErrorsView:output_type -> lapp.web.v1.GetErrorsViewResponse + 56, // [56:72] is the sub-list for method output_type + 40, // [40:56] is the sub-list for method input_type + 40, // [40:40] is the sub-list for extension type_name + 40, // [40:40] is the sub-list for extension extendee + 0, // [0:40] is the sub-list for field type_name } func init() { file_lapp_web_v1_web_proto_init() } @@ -2417,13 +3138,16 @@ func file_lapp_web_v1_web_proto_init() { if File_lapp_web_v1_web_proto != nil { return } + file_lapp_web_v1_web_proto_msgTypes[34].OneofWrappers = []any{ + (*ImportLogsRequest_GcpLogging)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_lapp_web_v1_web_proto_rawDesc), len(file_lapp_web_v1_web_proto_rawDesc)), - NumEnums: 3, - NumMessages: 36, + NumEnums: 4, + NumMessages: 46, NumExtensions: 0, NumServices: 1, }, diff --git a/gen/go/lapp/web/v1/webv1connect/web.connect.go b/gen/go/lapp/web/v1/webv1connect/web.connect.go index 5c1d2a1..54c001e 100644 --- a/gen/go/lapp/web/v1/webv1connect/web.connect.go +++ b/gen/go/lapp/web/v1/webv1connect/web.connect.go @@ -63,6 +63,15 @@ const ( // WorkspaceServiceListDiscoveryRunsProcedure is the fully-qualified name of the WorkspaceService's // ListDiscoveryRuns RPC. WorkspaceServiceListDiscoveryRunsProcedure = "/lapp.web.v1.WorkspaceService/ListDiscoveryRuns" + // WorkspaceServiceImportLogsProcedure is the fully-qualified name of the WorkspaceService's + // ImportLogs RPC. + WorkspaceServiceImportLogsProcedure = "/lapp.web.v1.WorkspaceService/ImportLogs" + // WorkspaceServiceGetImportRunProcedure is the fully-qualified name of the WorkspaceService's + // GetImportRun RPC. + WorkspaceServiceGetImportRunProcedure = "/lapp.web.v1.WorkspaceService/GetImportRun" + // WorkspaceServiceListImportRunsProcedure is the fully-qualified name of the WorkspaceService's + // ListImportRuns RPC. + WorkspaceServiceListImportRunsProcedure = "/lapp.web.v1.WorkspaceService/ListImportRuns" // WorkspaceServiceListPatternsProcedure is the fully-qualified name of the WorkspaceService's // ListPatterns RPC. WorkspaceServiceListPatternsProcedure = "/lapp.web.v1.WorkspaceService/ListPatterns" @@ -86,6 +95,9 @@ type WorkspaceServiceClient interface { CreateDiscoveryRun(context.Context, *connect.Request[v1.CreateDiscoveryRunRequest]) (*connect.Response[v1.CreateDiscoveryRunResponse], error) GetDiscoveryRun(context.Context, *connect.Request[v1.GetDiscoveryRunRequest]) (*connect.Response[v1.GetDiscoveryRunResponse], error) ListDiscoveryRuns(context.Context, *connect.Request[v1.ListDiscoveryRunsRequest]) (*connect.Response[v1.ListDiscoveryRunsResponse], error) + ImportLogs(context.Context, *connect.Request[v1.ImportLogsRequest]) (*connect.Response[v1.ImportLogsResponse], error) + GetImportRun(context.Context, *connect.Request[v1.GetImportRunRequest]) (*connect.Response[v1.GetImportRunResponse], error) + ListImportRuns(context.Context, *connect.Request[v1.ListImportRunsRequest]) (*connect.Response[v1.ListImportRunsResponse], error) ListPatterns(context.Context, *connect.Request[v1.ListPatternsRequest]) (*connect.Response[v1.ListPatternsResponse], error) GetPattern(context.Context, *connect.Request[v1.GetPatternRequest]) (*connect.Response[v1.GetPatternResponse], error) GetErrorsView(context.Context, *connect.Request[v1.GetErrorsViewRequest]) (*connect.Response[v1.GetErrorsViewResponse], error) @@ -162,6 +174,24 @@ func NewWorkspaceServiceClient(httpClient connect.HTTPClient, baseURL string, op connect.WithSchema(workspaceServiceMethods.ByName("ListDiscoveryRuns")), connect.WithClientOptions(opts...), ), + importLogs: connect.NewClient[v1.ImportLogsRequest, v1.ImportLogsResponse]( + httpClient, + baseURL+WorkspaceServiceImportLogsProcedure, + connect.WithSchema(workspaceServiceMethods.ByName("ImportLogs")), + connect.WithClientOptions(opts...), + ), + getImportRun: connect.NewClient[v1.GetImportRunRequest, v1.GetImportRunResponse]( + httpClient, + baseURL+WorkspaceServiceGetImportRunProcedure, + connect.WithSchema(workspaceServiceMethods.ByName("GetImportRun")), + connect.WithClientOptions(opts...), + ), + listImportRuns: connect.NewClient[v1.ListImportRunsRequest, v1.ListImportRunsResponse]( + httpClient, + baseURL+WorkspaceServiceListImportRunsProcedure, + connect.WithSchema(workspaceServiceMethods.ByName("ListImportRuns")), + connect.WithClientOptions(opts...), + ), listPatterns: connect.NewClient[v1.ListPatternsRequest, v1.ListPatternsResponse]( httpClient, baseURL+WorkspaceServiceListPatternsProcedure, @@ -195,6 +225,9 @@ type workspaceServiceClient struct { createDiscoveryRun *connect.Client[v1.CreateDiscoveryRunRequest, v1.CreateDiscoveryRunResponse] getDiscoveryRun *connect.Client[v1.GetDiscoveryRunRequest, v1.GetDiscoveryRunResponse] listDiscoveryRuns *connect.Client[v1.ListDiscoveryRunsRequest, v1.ListDiscoveryRunsResponse] + importLogs *connect.Client[v1.ImportLogsRequest, v1.ImportLogsResponse] + getImportRun *connect.Client[v1.GetImportRunRequest, v1.GetImportRunResponse] + listImportRuns *connect.Client[v1.ListImportRunsRequest, v1.ListImportRunsResponse] listPatterns *connect.Client[v1.ListPatternsRequest, v1.ListPatternsResponse] getPattern *connect.Client[v1.GetPatternRequest, v1.GetPatternResponse] getErrorsView *connect.Client[v1.GetErrorsViewRequest, v1.GetErrorsViewResponse] @@ -250,6 +283,21 @@ func (c *workspaceServiceClient) ListDiscoveryRuns(ctx context.Context, req *con return c.listDiscoveryRuns.CallUnary(ctx, req) } +// ImportLogs calls lapp.web.v1.WorkspaceService.ImportLogs. +func (c *workspaceServiceClient) ImportLogs(ctx context.Context, req *connect.Request[v1.ImportLogsRequest]) (*connect.Response[v1.ImportLogsResponse], error) { + return c.importLogs.CallUnary(ctx, req) +} + +// GetImportRun calls lapp.web.v1.WorkspaceService.GetImportRun. +func (c *workspaceServiceClient) GetImportRun(ctx context.Context, req *connect.Request[v1.GetImportRunRequest]) (*connect.Response[v1.GetImportRunResponse], error) { + return c.getImportRun.CallUnary(ctx, req) +} + +// ListImportRuns calls lapp.web.v1.WorkspaceService.ListImportRuns. +func (c *workspaceServiceClient) ListImportRuns(ctx context.Context, req *connect.Request[v1.ListImportRunsRequest]) (*connect.Response[v1.ListImportRunsResponse], error) { + return c.listImportRuns.CallUnary(ctx, req) +} + // ListPatterns calls lapp.web.v1.WorkspaceService.ListPatterns. func (c *workspaceServiceClient) ListPatterns(ctx context.Context, req *connect.Request[v1.ListPatternsRequest]) (*connect.Response[v1.ListPatternsResponse], error) { return c.listPatterns.CallUnary(ctx, req) @@ -277,6 +325,9 @@ type WorkspaceServiceHandler interface { CreateDiscoveryRun(context.Context, *connect.Request[v1.CreateDiscoveryRunRequest]) (*connect.Response[v1.CreateDiscoveryRunResponse], error) GetDiscoveryRun(context.Context, *connect.Request[v1.GetDiscoveryRunRequest]) (*connect.Response[v1.GetDiscoveryRunResponse], error) ListDiscoveryRuns(context.Context, *connect.Request[v1.ListDiscoveryRunsRequest]) (*connect.Response[v1.ListDiscoveryRunsResponse], error) + ImportLogs(context.Context, *connect.Request[v1.ImportLogsRequest]) (*connect.Response[v1.ImportLogsResponse], error) + GetImportRun(context.Context, *connect.Request[v1.GetImportRunRequest]) (*connect.Response[v1.GetImportRunResponse], error) + ListImportRuns(context.Context, *connect.Request[v1.ListImportRunsRequest]) (*connect.Response[v1.ListImportRunsResponse], error) ListPatterns(context.Context, *connect.Request[v1.ListPatternsRequest]) (*connect.Response[v1.ListPatternsResponse], error) GetPattern(context.Context, *connect.Request[v1.GetPatternRequest]) (*connect.Response[v1.GetPatternResponse], error) GetErrorsView(context.Context, *connect.Request[v1.GetErrorsViewRequest]) (*connect.Response[v1.GetErrorsViewResponse], error) @@ -349,6 +400,24 @@ func NewWorkspaceServiceHandler(svc WorkspaceServiceHandler, opts ...connect.Han connect.WithSchema(workspaceServiceMethods.ByName("ListDiscoveryRuns")), connect.WithHandlerOptions(opts...), ) + workspaceServiceImportLogsHandler := connect.NewUnaryHandler( + WorkspaceServiceImportLogsProcedure, + svc.ImportLogs, + connect.WithSchema(workspaceServiceMethods.ByName("ImportLogs")), + connect.WithHandlerOptions(opts...), + ) + workspaceServiceGetImportRunHandler := connect.NewUnaryHandler( + WorkspaceServiceGetImportRunProcedure, + svc.GetImportRun, + connect.WithSchema(workspaceServiceMethods.ByName("GetImportRun")), + connect.WithHandlerOptions(opts...), + ) + workspaceServiceListImportRunsHandler := connect.NewUnaryHandler( + WorkspaceServiceListImportRunsProcedure, + svc.ListImportRuns, + connect.WithSchema(workspaceServiceMethods.ByName("ListImportRuns")), + connect.WithHandlerOptions(opts...), + ) workspaceServiceListPatternsHandler := connect.NewUnaryHandler( WorkspaceServiceListPatternsProcedure, svc.ListPatterns, @@ -389,6 +458,12 @@ func NewWorkspaceServiceHandler(svc WorkspaceServiceHandler, opts ...connect.Han workspaceServiceGetDiscoveryRunHandler.ServeHTTP(w, r) case WorkspaceServiceListDiscoveryRunsProcedure: workspaceServiceListDiscoveryRunsHandler.ServeHTTP(w, r) + case WorkspaceServiceImportLogsProcedure: + workspaceServiceImportLogsHandler.ServeHTTP(w, r) + case WorkspaceServiceGetImportRunProcedure: + workspaceServiceGetImportRunHandler.ServeHTTP(w, r) + case WorkspaceServiceListImportRunsProcedure: + workspaceServiceListImportRunsHandler.ServeHTTP(w, r) case WorkspaceServiceListPatternsProcedure: workspaceServiceListPatternsHandler.ServeHTTP(w, r) case WorkspaceServiceGetPatternProcedure: @@ -444,6 +519,18 @@ func (UnimplementedWorkspaceServiceHandler) ListDiscoveryRuns(context.Context, * return nil, connect.NewError(connect.CodeUnimplemented, errors.New("lapp.web.v1.WorkspaceService.ListDiscoveryRuns is not implemented")) } +func (UnimplementedWorkspaceServiceHandler) ImportLogs(context.Context, *connect.Request[v1.ImportLogsRequest]) (*connect.Response[v1.ImportLogsResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("lapp.web.v1.WorkspaceService.ImportLogs is not implemented")) +} + +func (UnimplementedWorkspaceServiceHandler) GetImportRun(context.Context, *connect.Request[v1.GetImportRunRequest]) (*connect.Response[v1.GetImportRunResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("lapp.web.v1.WorkspaceService.GetImportRun is not implemented")) +} + +func (UnimplementedWorkspaceServiceHandler) ListImportRuns(context.Context, *connect.Request[v1.ListImportRunsRequest]) (*connect.Response[v1.ListImportRunsResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("lapp.web.v1.WorkspaceService.ListImportRuns is not implemented")) +} + func (UnimplementedWorkspaceServiceHandler) ListPatterns(context.Context, *connect.Request[v1.ListPatternsRequest]) (*connect.Response[v1.ListPatternsResponse], error) { return nil, connect.NewError(connect.CodeUnimplemented, errors.New("lapp.web.v1.WorkspaceService.ListPatterns is not implemented")) } diff --git a/go.mod b/go.mod index 1a43afd..15491b2 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/strrl/lapp go 1.25.7 require ( + cloud.google.com/go/logging v1.18.0 connectrpc.com/connect v1.20.0 github.com/bytedance/sonic v1.15.0 github.com/cloudwego/eino v0.8.0 @@ -16,13 +17,20 @@ require ( github.com/spf13/cobra v1.10.2 github.com/strrl/eino-acp v0.0.0-20260320032654-943782f485e5 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 - go.opentelemetry.io/otel v1.40.0 + go.opentelemetry.io/otel v1.43.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 - go.opentelemetry.io/otel/sdk v1.40.0 + go.opentelemetry.io/otel/sdk v1.43.0 + google.golang.org/api v0.274.0 google.golang.org/protobuf v1.36.11 ) require ( + cloud.google.com/go v0.123.0 // indirect + cloud.google.com/go/auth v0.19.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect + cloud.google.com/go/iam v1.7.0 // indirect + cloud.google.com/go/longrunning v0.9.0 // indirect github.com/apache/arrow-go/v18 v18.5.1 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect @@ -50,6 +58,9 @@ require ( github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/google/flatbuffers v25.12.19+incompatible // indirect + github.com/google/s2a-go v0.1.9 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect + github.com/googleapis/gax-go/v2 v2.21.0 // indirect github.com/goph/emperror v0.17.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect @@ -77,22 +88,27 @@ require ( github.com/yargevad/filepathx v1.0.0 // indirect github.com/zeebo/xxh3 v1.1.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect - go.opentelemetry.io/otel/metric v1.40.0 // indirect - go.opentelemetry.io/otel/trace v1.40.0 // indirect + go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/otel/trace v1.43.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect golang.org/x/arch v0.12.0 // indirect + golang.org/x/crypto v0.49.0 // indirect golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect - golang.org/x/mod v0.32.0 // indirect - golang.org/x/net v0.49.0 // indirect - golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.40.0 // indirect - golang.org/x/telemetry v0.0.0-20260116145544-c6413dc483f5 // indirect - golang.org/x/text v0.33.0 // indirect - golang.org/x/tools v0.41.0 // indirect + golang.org/x/mod v0.33.0 // indirect + golang.org/x/net v0.52.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 // indirect + golang.org/x/text v0.35.0 // indirect + golang.org/x/time v0.15.0 // indirect + golang.org/x/tools v0.42.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect - google.golang.org/grpc v1.78.0 // indirect + google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect + google.golang.org/grpc v1.80.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 6ddc462..426af53 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,31 @@ +cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= +cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= +cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= +cloud.google.com/go/auth v0.19.0 h1:DGYwtbcsGsT1ywuxsIoWi1u/vlks0moIblQHgSDgQkQ= +cloud.google.com/go/auth v0.19.0/go.mod h1:2Aph7BT2KnaSFOM0JDPyiYgNh6PL9vGMiP8CUIXZ+IY= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +cloud.google.com/go/iam v1.7.0 h1:JD3zh0C6LHl16aCn5Akff0+GELdp1+4hmh6ndoFLl8U= +cloud.google.com/go/iam v1.7.0/go.mod h1:tetWZW1PD/m6vcuY2Zj/aU0eCHNPuxedbnbRTyKXvdY= +cloud.google.com/go/logging v1.18.0 h1:KhzZq+1cSkPH9YUaKLLhLtQxIHitVayBmk0sGfoM9+k= +cloud.google.com/go/logging v1.18.0/go.mod h1:ZGKnpBaURITh+g/uom2VhbiFoFWvejcrHPDhxFtU/gI= +cloud.google.com/go/longrunning v0.9.0 h1:0EzbDEGsAvOZNbqXopgniY0w0a1phvu5IdUFq8grmqY= +cloud.google.com/go/longrunning v0.9.0/go.mod h1:pkTz846W7bF4o2SzdWJ40Hu0Re+UoNT6Q5t+igIcb8E= +cloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE= +cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI= +cloud.google.com/go/storage v1.62.0 h1:w2pQJhpUqVerMON45vatE2FpCYsNTf7OHjkn6ux5mMU= +cloud.google.com/go/storage v1.62.0/go.mod h1:T5hz3qzcpnxZ5LdKc7y8Tw7lh4v9zeeVyrD/cLJAzZU= connectrpc.com/connect v1.20.0 h1:6TNDAB+WeNd2uolWNlYczB5E0KNNaVMNUEx8JEUsPmQ= connectrpc.com/connect v1.20.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 h1:UnDZ/zFfG1JhH/DqxIZYU/1CUAlTUScoXD/LcM2Ykk8= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0/go.mod h1:IA1C1U7jO/ENqm/vhi7V9YYpBsp+IMyqNrEN94N7tVc= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 h1:0s6TxfCu2KHkkZPnBfsQ2y5qia0jl3MMrmBhu3nCOYk= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc= github.com/airbrake/gobrake v3.6.1+incompatible/go.mod h1:wM4gu3Cn0W0K7GUuVWnlXZU11AGBXMILnrdOU8Kn00o= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= @@ -42,6 +68,8 @@ github.com/cloudwego/eino-ext/libs/acl/langfuse v0.0.0-20251124083837-ce2e7e196f github.com/cloudwego/eino-ext/libs/acl/langfuse v0.0.0-20251124083837-ce2e7e196f9f/go.mod h1:P3zzJTRexY0QKaE9Vn2CmOnCorIMgNzNtler8mw9IQM= github.com/cloudwego/eino-ext/libs/acl/openai v0.1.13 h1:z0bI5TH3nE+uDQiRhxBQMvk2HswlDUM3xP38+VSgpSQ= github.com/cloudwego/eino-ext/libs/acl/openai v0.1.13/go.mod h1:1xMQZ8eE11pkEoTAEy8UlaAY817qGVMvjpDPGSIO3Ns= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= +github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= github.com/coder/acp-go-sdk v0.6.3 h1:LsXQytehdjKIYJnoVWON/nf7mqbiarnyuyE3rrjBsXQ= github.com/coder/acp-go-sdk v0.6.3/go.mod h1:yKzM/3R9uELp4+nBAwwtkS0aN1FOFjo11CNPy37yFko= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -67,6 +95,11 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/eino-contrib/jsonschema v1.0.3 h1:2Kfsm1xlMV0ssY2nuxshS4AwbLFuqmPmzIjLVJ1Fsp0= github.com/eino-contrib/jsonschema v1.0.3/go.mod h1:cpnX4SyKjWjGC7iN2EbhxaTdLqGjCi0e9DxpLYxddD4= +github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= +github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= +github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= +github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4= +github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -77,6 +110,8 @@ github.com/go-check/check v0.0.0-20180628173108-788fd7840127 h1:0gkP6mzaMqkmpcJY github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= +github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -87,6 +122,8 @@ github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlnd github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -99,8 +136,14 @@ github.com/google/flatbuffers v25.12.19+incompatible/go.mod h1:1AeVuKshWv4vARoZa github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8= +github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= +github.com/googleapis/gax-go/v2 v2.21.0 h1:h45NjjzEO3faG9Lg/cFrBh2PgegVVgzqKzuZl/wMbiI= +github.com/googleapis/gax-go/v2 v2.21.0/go.mod h1:But/NJU6TnZsrLai/xBAQLLz+Hc7fHZJt/hsCz3Fih4= github.com/goph/emperror v0.17.2 h1:yLapQcmEsO0ipe9p5TaN22djm3OFV/TfM/fcYP0/J18= github.com/goph/emperror v0.17.2/go.mod h1:+ZbQ+fUNO/6FNiUo0ujtMjhgad9Xa6fQL9KhH4LNHic= github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= @@ -168,6 +211,8 @@ github.com/pierrec/lz4/v4 v4.1.25/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcR github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -188,6 +233,8 @@ github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= +github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -224,24 +271,30 @@ github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE= +go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0= -go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= -go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 h1:wVZXIWjQSeSmMoxF74LzAnpVQOAFDo3pPji9Y4SOFKc= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0/go.mod h1:khvBS2IggMFNwZK/6lEeHg/W57h/IX6J4URh57fuI40= -go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= -go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= -go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= -go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= -go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= -go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= -go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= -go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -252,42 +305,50 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg= golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= -golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= -golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= -golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= +golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= -golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= -golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/telemetry v0.0.0-20260116145544-c6413dc483f5 h1:i0p03B68+xC1kD2QUO8JzDTPXCzhN56OLJ+IhHY8U3A= -golang.org/x/telemetry v0.0.0-20260116145544-c6413dc483f5/go.mod h1:b7fPSJ0pKZ3ccUh8gnTONJxhn3c/PS6tyzQvyqw4iA8= -golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= -golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 h1:bTLqdHv7xrGlFbvf5/TXNxy/iUwwdkjhqQTJDjW7aj0= +golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4/go.mod h1:g5NllXBEermZrmR51cJDQxmJUHUOfRAaNyWBM+R+548= +golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= -golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= -golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= -golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= +golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M= -google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= -google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= -google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/api v0.274.0 h1:aYhycS5QQCwxHLwfEHRRLf9yNsfvp1JadKKWBE54RFA= +google.golang.org/api v0.274.0/go.mod h1:JbAt7mF+XVmWu6xNP8/+CTiGH30ofmCmk9nM8d8fHew= +google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0= +google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I= +google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= +google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= +google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/pkg/gcplog/fetch.go b/pkg/gcplog/fetch.go new file mode 100644 index 0000000..877e9e2 --- /dev/null +++ b/pkg/gcplog/fetch.go @@ -0,0 +1,171 @@ +// Package gcplog fetches log entries from Google Cloud Logging and renders +// them as plain text lines suitable for the workspace discovery pipeline. +package gcplog + +import ( + "context" + "encoding/json" + "fmt" + "io" + "strings" + "time" + + "cloud.google.com/go/logging" + "cloud.google.com/go/logging/logadmin" + "github.com/go-errors/errors" + "google.golang.org/api/iterator" + "google.golang.org/protobuf/types/known/structpb" +) + +// progressInterval controls how many entries are fetched between +// OnProgress callbacks. +const progressInterval = 500 + +// FetchConfig configures a single fetch from Cloud Logging. +type FetchConfig struct { + // ProjectID is the GCP project to read logs from. + ProjectID string + // Filter is an optional Cloud Logging filter expression. It is combined + // with the time range using AND. + Filter string + // Since is the inclusive lower bound of the time range. + Since time.Time + // Until is the optional inclusive upper bound. Zero means now. + Until time.Time + // Limit caps the number of fetched entries and must be positive. + Limit int + // OnProgress is invoked periodically with the number of entries + // fetched so far. + OnProgress func(fetched int) +} + +func (c FetchConfig) validate() error { + if c.ProjectID == "" { + return errors.New("gcp project id is required") + } + if c.Since.IsZero() { + return errors.New("since time is required") + } + if c.Limit <= 0 { + return errors.New("limit must be positive") + } + if !c.Until.IsZero() && c.Until.Before(c.Since) { + return errors.New("until must not be before since") + } + return nil +} + +// Fetcher fetches Cloud Logging entries and writes rendered text lines. +type Fetcher interface { + Fetch(ctx context.Context, cfg FetchConfig, w io.Writer) (int, error) +} + +var _ Fetcher = (*logadminFetcher)(nil) + +// logadminFetcher reads entries through the Cloud Logging admin API using +// Application Default Credentials. +type logadminFetcher struct{} + +// NewFetcher returns a Fetcher backed by the Cloud Logging admin API. +func NewFetcher() Fetcher { + return &logadminFetcher{} +} + +func (f *logadminFetcher) Fetch(ctx context.Context, cfg FetchConfig, w io.Writer) (int, error) { + if err := cfg.validate(); err != nil { + return 0, err + } + client, err := logadmin.NewClient(ctx, cfg.ProjectID) + if err != nil { + return 0, errors.Errorf("create cloud logging client: %w", err) + } + defer func() { _ = client.Close() }() + + it := client.Entries(ctx, logadmin.Filter(BuildFilter(cfg))) + return drainEntries(it, cfg, w) +} + +// entryIterator abstracts logadmin's entry iterator for testing. +type entryIterator interface { + Next() (*logging.Entry, error) +} + +func drainEntries(it entryIterator, cfg FetchConfig, w io.Writer) (int, error) { + count := 0 + for count < cfg.Limit { + entry, err := it.Next() + if errors.Is(err, iterator.Done) { + break + } + if err != nil { + return count, errors.Errorf("fetch cloud logging entries: %w", err) + } + if _, err := io.WriteString(w, RenderEntry(entry)+"\n"); err != nil { + return count, errors.Errorf("write log line: %w", err) + } + count++ + if cfg.OnProgress != nil && count%progressInterval == 0 { + cfg.OnProgress(count) + } + } + return count, nil +} + +// BuildFilter combines the configured time range and user filter into a +// single Cloud Logging filter expression. +func BuildFilter(cfg FetchConfig) string { + parts := []string{fmt.Sprintf("timestamp>=%q", cfg.Since.UTC().Format(time.RFC3339Nano))} + if !cfg.Until.IsZero() { + parts = append(parts, fmt.Sprintf("timestamp<=%q", cfg.Until.UTC().Format(time.RFC3339Nano))) + } + if strings.TrimSpace(cfg.Filter) != "" { + parts = append(parts, "("+strings.TrimSpace(cfg.Filter)+")") + } + return strings.Join(parts, " AND ") +} + +// RenderEntry renders one Cloud Logging entry as a single text line with an +// RFC3339 timestamp prefix so the multiline detector recognizes entry +// boundaries. +func RenderEntry(entry *logging.Entry) string { + ts := entry.Timestamp.UTC().Format(time.RFC3339Nano) + severity := strings.ToUpper(entry.Severity.String()) + payload := renderPayload(entry.Payload) + return ts + " " + severity + " " + payload +} + +func renderPayload(payload any) string { + switch value := payload.(type) { + case nil: + return "" + case string: + return value + case *structpb.Struct: + if message := structMessage(value); message != "" { + return message + } + data, err := value.MarshalJSON() + if err != nil { + return value.String() + } + return string(data) + default: + data, err := json.Marshal(value) + if err != nil { + return fmt.Sprintf("%v", value) + } + return string(data) + } +} + +// structMessage extracts a conventional message field from a JSON payload. +func structMessage(payload *structpb.Struct) string { + for _, key := range []string{"message", "msg"} { + if field, ok := payload.GetFields()[key]; ok { + if text := field.GetStringValue(); text != "" { + return text + } + } + } + return "" +} diff --git a/pkg/gcplog/fetch_test.go b/pkg/gcplog/fetch_test.go new file mode 100644 index 0000000..7254088 --- /dev/null +++ b/pkg/gcplog/fetch_test.go @@ -0,0 +1,196 @@ +package gcplog + +import ( + "strings" + "testing" + "time" + + "cloud.google.com/go/logging" + "google.golang.org/api/iterator" + "google.golang.org/protobuf/types/known/structpb" +) + +func TestBuildFilter(t *testing.T) { + since := time.Date(2026, 7, 1, 10, 0, 0, 0, time.UTC) + until := time.Date(2026, 7, 1, 11, 0, 0, 0, time.UTC) + + tests := []struct { + name string + cfg FetchConfig + want string + }{ + { + name: "since only", + cfg: FetchConfig{Since: since}, + want: `timestamp>="2026-07-01T10:00:00Z"`, + }, + { + name: "since and until", + cfg: FetchConfig{Since: since, Until: until}, + want: `timestamp>="2026-07-01T10:00:00Z" AND timestamp<="2026-07-01T11:00:00Z"`, + }, + { + name: "with user filter", + cfg: FetchConfig{Since: since, Filter: `severity>=ERROR OR resource.type="k8s_container"`}, + want: `timestamp>="2026-07-01T10:00:00Z" AND (severity>=ERROR OR resource.type="k8s_container")`, + }, + { + name: "user filter is trimmed", + cfg: FetchConfig{Since: since, Filter: " severity>=ERROR "}, + want: `timestamp>="2026-07-01T10:00:00Z" AND (severity>=ERROR)`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := BuildFilter(tt.cfg); got != tt.want { + t.Errorf("BuildFilter() = %q, want %q", got, tt.want) + } + }) + } +} + +func TestRenderEntry(t *testing.T) { + ts := time.Date(2026, 7, 1, 10, 30, 45, 123000000, time.UTC) + + jsonPayload, err := structpb.NewStruct(map[string]any{ + "message": "request failed", + "code": 500, + }) + if err != nil { + t.Fatal(err) + } + noMessagePayload, err := structpb.NewStruct(map[string]any{ + "code": 500, + }) + if err != nil { + t.Fatal(err) + } + + tests := []struct { + name string + entry *logging.Entry + want string + }{ + { + name: "text payload", + entry: &logging.Entry{Timestamp: ts, Severity: logging.Error, Payload: "connection refused"}, + want: "2026-07-01T10:30:45.123Z ERROR connection refused", + }, + { + name: "json payload with message field", + entry: &logging.Entry{Timestamp: ts, Severity: logging.Warning, Payload: jsonPayload}, + want: "2026-07-01T10:30:45.123Z WARNING request failed", + }, + { + name: "json payload without message field", + entry: &logging.Entry{Timestamp: ts, Severity: logging.Info, Payload: noMessagePayload}, + want: `2026-07-01T10:30:45.123Z INFO {"code":500}`, + }, + { + name: "nil payload", + entry: &logging.Entry{Timestamp: ts, Severity: logging.Default}, + want: "2026-07-01T10:30:45.123Z DEFAULT ", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := RenderEntry(tt.entry); got != tt.want { + t.Errorf("RenderEntry() = %q, want %q", got, tt.want) + } + }) + } +} + +type fakeIterator struct { + entries []*logging.Entry + index int +} + +func (f *fakeIterator) Next() (*logging.Entry, error) { + if f.index >= len(f.entries) { + return nil, iterator.Done + } + entry := f.entries[f.index] + f.index++ + return entry, nil +} + +func TestDrainEntries(t *testing.T) { + ts := time.Date(2026, 7, 1, 10, 0, 0, 0, time.UTC) + makeEntries := func(n int) []*logging.Entry { + entries := make([]*logging.Entry, n) + for i := range entries { + entries[i] = &logging.Entry{Timestamp: ts, Severity: logging.Info, Payload: "line"} + } + return entries + } + + t.Run("drains all entries under limit", func(t *testing.T) { + var sb strings.Builder + count, err := drainEntries(&fakeIterator{entries: makeEntries(3)}, FetchConfig{Limit: 10}, &sb) + if err != nil { + t.Fatal(err) + } + if count != 3 { + t.Errorf("count = %d, want 3", count) + } + if lines := strings.Count(sb.String(), "\n"); lines != 3 { + t.Errorf("lines = %d, want 3", lines) + } + }) + + t.Run("stops at limit", func(t *testing.T) { + var sb strings.Builder + count, err := drainEntries(&fakeIterator{entries: makeEntries(10)}, FetchConfig{Limit: 4}, &sb) + if err != nil { + t.Fatal(err) + } + if count != 4 { + t.Errorf("count = %d, want 4", count) + } + }) + + t.Run("reports progress", func(t *testing.T) { + var sb strings.Builder + var reported []int + cfg := FetchConfig{ + Limit: progressInterval * 2, + OnProgress: func(fetched int) { + reported = append(reported, fetched) + }, + } + count, err := drainEntries(&fakeIterator{entries: makeEntries(progressInterval * 2)}, cfg, &sb) + if err != nil { + t.Fatal(err) + } + if count != progressInterval*2 { + t.Errorf("count = %d, want %d", count, progressInterval*2) + } + if len(reported) != 2 || reported[0] != progressInterval || reported[1] != progressInterval*2 { + t.Errorf("reported = %v, want [%d %d]", reported, progressInterval, progressInterval*2) + } + }) +} + +func TestFetchConfigValidate(t *testing.T) { + since := time.Date(2026, 7, 1, 10, 0, 0, 0, time.UTC) + + tests := []struct { + name string + cfg FetchConfig + wantErr bool + }{ + {name: "valid", cfg: FetchConfig{ProjectID: "p", Since: since, Limit: 10}, wantErr: false}, + {name: "missing project", cfg: FetchConfig{Since: since, Limit: 10}, wantErr: true}, + {name: "missing since", cfg: FetchConfig{ProjectID: "p", Limit: 10}, wantErr: true}, + {name: "zero limit", cfg: FetchConfig{ProjectID: "p", Since: since}, wantErr: true}, + {name: "until before since", cfg: FetchConfig{ProjectID: "p", Since: since, Until: since.Add(-time.Hour), Limit: 10}, wantErr: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.cfg.validate(); (err != nil) != tt.wantErr { + t.Errorf("validate() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/pkg/webapp/import.go b/pkg/webapp/import.go new file mode 100644 index 0000000..3c30eac --- /dev/null +++ b/pkg/webapp/import.go @@ -0,0 +1,289 @@ +package webapp + +import ( + "context" + "fmt" + "log/slog" + "os" + "path/filepath" + "sort" + "strings" + "time" + + "connectrpc.com/connect" + "github.com/go-errors/errors" + "github.com/google/uuid" + webv1 "github.com/strrl/lapp/gen/go/lapp/web/v1" + "github.com/strrl/lapp/pkg/gcplog" + "github.com/strrl/lapp/pkg/workspace" + "google.golang.org/protobuf/types/known/timestamppb" +) + +const defaultImportLimit = 10000 +const maxImportLimit = 100000 +const defaultImportWindow = time.Hour + +func (s *WorkspaceService) ImportLogs(_ context.Context, req *connect.Request[webv1.ImportLogsRequest]) (*connect.Response[webv1.ImportLogsResponse], error) { + id, connectErr := workspaceIDFromName(req.Msg.Parent) + if connectErr != nil { + return nil, connectErr + } + if connectErr := s.ensureWorkspaceExists(id); connectErr != nil { + return nil, connectErr + } + gcp := req.Msg.GetGcpLogging() + if gcp == nil { + return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("gcp_logging source is required")) + } + if strings.TrimSpace(gcp.ProjectId) == "" { + return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("project_id is required")) + } + limit := int(gcp.Limit) + if limit <= 0 { + limit = defaultImportLimit + } + if limit > maxImportLimit { + limit = maxImportLimit + } + since := time.Now().Add(-defaultImportWindow).UTC() + if gcp.StartTime != nil { + since = gcp.StartTime.AsTime() + } + var until time.Time + if gcp.EndTime != nil { + until = gcp.EndTime.AsTime() + } + if !until.IsZero() && until.Before(since) { + return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("end_time must not be before start_time")) + } + if !s.reserveDiscovery(id) { + return nil, connect.NewError(connect.CodeFailedPrecondition, errors.New("another job is already running for this workspace")) + } + runID, uuidErr := uuid.NewV7() + if uuidErr != nil { + s.clearRunning(id) + return nil, connect.NewError(connect.CodeInternal, uuidErr) + } + record := workspace.ImportRunRecord{ + ID: runID.String(), + State: workspace.ImportRunStateRunning, + Source: workspace.ImportSourceGCPLogging, + GCPProject: gcp.ProjectId, + Filter: gcp.Filter, + StartedAt: time.Now().UTC(), + } + if err := workspace.WriteImportRunRecord(s.workspaceDir(id), record); err != nil { + s.clearRunning(id) + return nil, connect.NewError(connect.CodeInternal, err) + } + cfg := gcplog.FetchConfig{ + ProjectID: gcp.ProjectId, + Filter: gcp.Filter, + Since: since, + Until: until, + Limit: limit, + } + go s.runGcpImport(id, record, cfg) + return connect.NewResponse(&webv1.ImportLogsResponse{ImportRun: s.importRunMessage(id, record)}), nil +} + +func (s *WorkspaceService) GetImportRun(_ context.Context, req *connect.Request[webv1.GetImportRunRequest]) (*connect.Response[webv1.GetImportRunResponse], error) { + id, runID, connectErr := importRunNameFromResource(req.Msg.Name) + if connectErr != nil { + return nil, connectErr + } + record, err := workspace.ReadImportRunRecord(s.workspaceDir(id), runID) + if err != nil { + return nil, connect.NewError(connect.CodeNotFound, err) + } + return connect.NewResponse(&webv1.GetImportRunResponse{ImportRun: s.importRunMessage(id, record)}), nil +} + +func (s *WorkspaceService) ListImportRuns(_ context.Context, req *connect.Request[webv1.ListImportRunsRequest]) (*connect.Response[webv1.ListImportRunsResponse], error) { + id, connectErr := workspaceIDFromName(req.Msg.Parent) + if connectErr != nil { + return nil, connectErr + } + if connectErr := s.ensureWorkspaceExists(id); connectErr != nil { + return nil, connectErr + } + records, err := workspace.ListImportRunRecords(s.workspaceDir(id)) + if err != nil { + return nil, connect.NewError(connect.CodeInternal, err) + } + sort.Slice(records, func(i, j int) bool { + return records[i].StartedAt.After(records[j].StartedAt) + }) + runs := make([]*webv1.ImportRun, 0, len(records)) + for _, record := range records { + runs = append(runs, s.importRunMessage(id, record)) + } + return connect.NewResponse(&webv1.ListImportRunsResponse{ImportRuns: runs}), nil +} + +// runGcpImport fetches entries into the import run directory, then moves the +// finished file into the workspace logs directory. +func (s *WorkspaceService) runGcpImport(id string, record workspace.ImportRunRecord, cfg gcplog.FetchConfig) { + defer s.clearRunning(id) + workspaceDir := s.workspaceDir(id) + slog.Info("ImportRun started", "workspace", id, "run", record.ID, "project", cfg.ProjectID) + + fail := func(code, message string) { + finishedAt := time.Now().UTC() + record.State = workspace.ImportRunStateFailed + record.FinishedAt = &finishedAt + record.Error = &workspace.ImportRunError{Code: code, Message: message} + if err := workspace.WriteImportRunRecord(workspaceDir, record); err != nil { + slog.Error("write import run record", "workspace", id, "run", record.ID, "error", err) + } + slog.Error("ImportRun failed", "workspace", id, "run", record.ID, "code", code, "message", message) + } + + fetchPath := filepath.Join(workspace.ImportRunDir(workspaceDir, record.ID), "fetch.log") + file, err := os.Create(fetchPath) + if err != nil { + fail("IMPORT_WRITE_FAILED", err.Error()) + return + } + + cfg.OnProgress = func(fetched int) { + record.Progress = &workspace.ImportRunProgress{FetchedCount: fetched} + if err := workspace.WriteImportRunRecord(workspaceDir, record); err != nil { + slog.Error("write import run progress", "workspace", id, "run", record.ID, "error", err) + } + } + + count, fetchErr := s.gcpFetcher.Fetch(context.Background(), cfg, file) + closeErr := file.Close() + if fetchErr != nil { + fail("GCP_FETCH_FAILED", fetchErr.Error()) + return + } + if closeErr != nil { + fail("IMPORT_WRITE_FAILED", closeErr.Error()) + return + } + if count == 0 { + fail("IMPORT_NO_ENTRIES", "no log entries matched the filter and time range") + return + } + + fileName := importLogFileName(workspaceDir, cfg.ProjectID, record.StartedAt, record.ID) + if err := os.Rename(fetchPath, filepath.Join(workspaceDir, "logs", fileName)); err != nil { + fail("IMPORT_WRITE_FAILED", err.Error()) + return + } + + finishedAt := time.Now().UTC() + record.State = workspace.ImportRunStateSucceeded + record.FinishedAt = &finishedAt + record.EntryCount = count + record.Progress = &workspace.ImportRunProgress{FetchedCount: count} + record.LogFileName = fileName + if err := workspace.WriteImportRunRecord(workspaceDir, record); err != nil { + slog.Error("write import run record", "workspace", id, "run", record.ID, "error", err) + return + } + slog.Info("ImportRun completed", "workspace", id, "run", record.ID, "entries", count, "file", fileName) +} + +// importLogFileName picks a readable, collision free file name for the +// imported log inside the workspace logs directory. +func importLogFileName(workspaceDir, projectID string, startedAt time.Time, runID string) string { + base := fmt.Sprintf("gcp-%s-%s", projectID, startedAt.UTC().Format("20060102-150405")) + name := base + ".log" + if _, err := os.Stat(filepath.Join(workspaceDir, "logs", name)); os.IsNotExist(err) { + return name + } + suffix := runID + if len(suffix) > 8 { + suffix = suffix[:8] + } + return base + "-" + suffix + ".log" +} + +func (s *WorkspaceService) importRunMessage(workspaceID string, record workspace.ImportRunRecord) *webv1.ImportRun { + msg := &webv1.ImportRun{ + Name: fmt.Sprintf("%s/importRuns/%s", workspaceName(workspaceID), record.ID), + ImportRunId: record.ID, + State: importRunState(record.State), + Source: record.Source, + StartedAt: timestamppb.New(record.StartedAt), + EntryCount: int32(record.EntryCount), + LogFileName: record.LogFileName, + } + if record.Progress != nil { + msg.Progress = &webv1.ImportRunProgress{FetchedCount: int32(record.Progress.FetchedCount)} + } + if record.Error != nil { + msg.Error = &webv1.ImportRunError{Code: record.Error.Code, Message: record.Error.Message} + } + if record.FinishedAt != nil { + msg.FinishedAt = timestamppb.New(*record.FinishedAt) + } + return msg +} + +func importRunNameFromResource(name string) (workspaceID, runID string, err *connect.Error) { + parts := strings.Split(strings.Trim(name, "/"), "/") + if len(parts) == 4 && parts[0] == "workspaces" && parts[2] == "importRuns" && parts[1] != "" && parts[3] != "" { + return parts[1], parts[3], nil + } + return "", "", connect.NewError(connect.CodeInvalidArgument, errors.Errorf("invalid import run name %q", name)) +} + +func importRunState(state workspace.ImportRunState) webv1.ImportRunState { + switch state { + case workspace.ImportRunStateQueued: + return webv1.ImportRunState_IMPORT_RUN_STATE_QUEUED + case workspace.ImportRunStateRunning: + return webv1.ImportRunState_IMPORT_RUN_STATE_RUNNING + case workspace.ImportRunStateSucceeded: + return webv1.ImportRunState_IMPORT_RUN_STATE_SUCCEEDED + case workspace.ImportRunStateFailed: + return webv1.ImportRunState_IMPORT_RUN_STATE_FAILED + default: + return webv1.ImportRunState_IMPORT_RUN_STATE_UNSPECIFIED + } +} + +func (s *WorkspaceService) recoverInterruptedImportRuns() error { + entries, err := os.ReadDir(s.root) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return errors.Errorf("read workspace root: %w", err) + } + + for _, entry := range entries { + if !entry.IsDir() { + continue + } + workspaceDir := s.workspaceDir(entry.Name()) + records, err := workspace.ListImportRunRecords(workspaceDir) + if err != nil { + return errors.Errorf("list import runs for %s: %w", entry.Name(), err) + } + for _, record := range records { + if !isInterruptedImportRun(record.State) { + continue + } + finishedAt := time.Now().UTC() + record.State = workspace.ImportRunStateFailed + record.FinishedAt = &finishedAt + record.Error = &workspace.ImportRunError{ + Code: "IMPORT_INTERRUPTED", + Message: "Import did not complete before the web server stopped", + } + if err := workspace.WriteImportRunRecord(workspaceDir, record); err != nil { + return errors.Errorf("mark import run %s interrupted: %w", record.ID, err) + } + } + } + return nil +} + +func isInterruptedImportRun(state workspace.ImportRunState) bool { + return state == workspace.ImportRunStateQueued || state == workspace.ImportRunStateRunning +} diff --git a/pkg/webapp/import_test.go b/pkg/webapp/import_test.go new file mode 100644 index 0000000..487996e --- /dev/null +++ b/pkg/webapp/import_test.go @@ -0,0 +1,246 @@ +package webapp + +import ( + "context" + "io" + "os" + "path/filepath" + "testing" + "time" + + "connectrpc.com/connect" + "github.com/go-errors/errors" + webv1 "github.com/strrl/lapp/gen/go/lapp/web/v1" + "github.com/strrl/lapp/pkg/gcplog" + "github.com/strrl/lapp/pkg/workspace" +) + +type fakeFetcher struct { + lines []string + err error + lastCfg gcplog.FetchConfig + release chan struct{} +} + +func (f *fakeFetcher) Fetch(_ context.Context, cfg gcplog.FetchConfig, w io.Writer) (int, error) { + f.lastCfg = cfg + if f.release != nil { + <-f.release + } + if f.err != nil { + return 0, f.err + } + for _, line := range f.lines { + if _, err := io.WriteString(w, line+"\n"); err != nil { + return 0, err + } + } + return len(f.lines), nil +} + +func newImportTestService(t *testing.T, fetcher gcplog.Fetcher) (service *WorkspaceService, workspaceName string) { + t.Helper() + service, err := NewWorkspaceService(ServiceConfig{ + Root: t.TempDir(), + GcpFetcher: fetcher, + }) + if err != nil { + t.Fatalf("NewWorkspaceService: %v", err) + } + created, createErr := service.CreateWorkspace(context.Background(), connect.NewRequest(&webv1.CreateWorkspaceRequest{ + WorkspaceId: "gcp-import", + })) + if createErr != nil { + t.Fatalf("CreateWorkspace: %v", createErr) + } + return service, created.Msg.Workspace.Name +} + +func importLogs(t *testing.T, service *WorkspaceService, parent string) *webv1.ImportRun { + t.Helper() + response, err := service.ImportLogs(context.Background(), connect.NewRequest(&webv1.ImportLogsRequest{ + Parent: parent, + Source: &webv1.ImportLogsRequest_GcpLogging{ + GcpLogging: &webv1.GcpLoggingSource{ + ProjectId: "my-project", + Filter: "severity>=ERROR", + }, + }, + })) + if err != nil { + t.Fatalf("ImportLogs: %v", err) + } + return response.Msg.ImportRun +} + +func waitForImportRunState(t *testing.T, service *WorkspaceService, runName string, state webv1.ImportRunState) *webv1.ImportRun { + t.Helper() + deadline := time.Now().Add(5 * time.Second) + for time.Now().Before(deadline) { + response, err := service.GetImportRun(context.Background(), connect.NewRequest(&webv1.GetImportRunRequest{Name: runName})) + if err != nil { + t.Fatalf("GetImportRun: %v", err) + } + if response.Msg.ImportRun.State == state { + return response.Msg.ImportRun + } + time.Sleep(20 * time.Millisecond) + } + t.Fatalf("import run did not reach %s", state) + return nil +} + +func TestImportLogsSuccessLandsFileInLogs(t *testing.T) { + fetcher := &fakeFetcher{lines: []string{ + "2026-07-01T10:00:00Z ERROR db timeout user=42", + "2026-07-01T10:00:01Z ERROR db timeout user=43", + }} + service, workspaceName := newImportTestService(t, fetcher) + + run := importLogs(t, service, workspaceName) + if run.State != webv1.ImportRunState_IMPORT_RUN_STATE_RUNNING { + t.Fatalf("expected running import, got %s", run.State) + } + if run.Source != workspace.ImportSourceGCPLogging { + t.Fatalf("source = %q", run.Source) + } + + final := waitForImportRunState(t, service, run.Name, webv1.ImportRunState_IMPORT_RUN_STATE_SUCCEEDED) + if final.EntryCount != 2 { + t.Fatalf("entry count = %d, want 2", final.EntryCount) + } + if final.LogFileName == "" { + t.Fatal("expected log file name") + } + + data, err := os.ReadFile(filepath.Join(service.workspaceDir("gcp-import"), "logs", final.LogFileName)) + if err != nil { + t.Fatalf("read imported file: %v", err) + } + if string(data) != "2026-07-01T10:00:00Z ERROR db timeout user=42\n2026-07-01T10:00:01Z ERROR db timeout user=43\n" { + t.Fatalf("unexpected file content: %q", string(data)) + } + + files, err := service.ListLogFiles(context.Background(), connect.NewRequest(&webv1.ListLogFilesRequest{Parent: workspaceName})) + if err != nil { + t.Fatalf("ListLogFiles: %v", err) + } + if len(files.Msg.LogFiles) != 1 || files.Msg.LogFiles[0].FileName != final.LogFileName { + t.Fatalf("unexpected log files: %+v", files.Msg.LogFiles) + } + + if fetcher.lastCfg.ProjectID != "my-project" || fetcher.lastCfg.Filter != "severity>=ERROR" { + t.Fatalf("unexpected fetch config: %+v", fetcher.lastCfg) + } + if fetcher.lastCfg.Limit != defaultImportLimit { + t.Fatalf("limit = %d, want default %d", fetcher.lastCfg.Limit, defaultImportLimit) + } +} + +func TestImportLogsFetchFailureMarksRunFailed(t *testing.T) { + fetcher := &fakeFetcher{err: errors.New("permission denied")} + service, workspaceName := newImportTestService(t, fetcher) + + run := importLogs(t, service, workspaceName) + final := waitForImportRunState(t, service, run.Name, webv1.ImportRunState_IMPORT_RUN_STATE_FAILED) + if final.Error == nil || final.Error.Code != "GCP_FETCH_FAILED" { + t.Fatalf("unexpected error: %+v", final.Error) + } + + files, err := service.ListLogFiles(context.Background(), connect.NewRequest(&webv1.ListLogFilesRequest{Parent: workspaceName})) + if err != nil { + t.Fatalf("ListLogFiles: %v", err) + } + if len(files.Msg.LogFiles) != 0 { + t.Fatalf("expected no log files, got %+v", files.Msg.LogFiles) + } +} + +func TestImportLogsZeroEntriesMarksRunFailed(t *testing.T) { + fetcher := &fakeFetcher{} + service, workspaceName := newImportTestService(t, fetcher) + + run := importLogs(t, service, workspaceName) + final := waitForImportRunState(t, service, run.Name, webv1.ImportRunState_IMPORT_RUN_STATE_FAILED) + if final.Error == nil || final.Error.Code != "IMPORT_NO_ENTRIES" { + t.Fatalf("unexpected error: %+v", final.Error) + } +} + +func TestImportLogsBlocksConcurrentJobs(t *testing.T) { + fetcher := &fakeFetcher{ + lines: []string{"2026-07-01T10:00:00Z ERROR boom"}, + release: make(chan struct{}), + } + service, workspaceName := newImportTestService(t, fetcher) + + run := importLogs(t, service, workspaceName) + + if _, err := service.ImportLogs(context.Background(), connect.NewRequest(&webv1.ImportLogsRequest{ + Parent: workspaceName, + Source: &webv1.ImportLogsRequest_GcpLogging{ + GcpLogging: &webv1.GcpLoggingSource{ProjectId: "my-project"}, + }, + })); connect.CodeOf(err) != connect.CodeFailedPrecondition { + t.Fatalf("expected concurrent import to be rejected, got %v", err) + } + if _, err := service.CreateDiscoveryRun(context.Background(), connect.NewRequest(&webv1.CreateDiscoveryRunRequest{ + Parent: workspaceName, + })); connect.CodeOf(err) != connect.CodeFailedPrecondition { + t.Fatalf("expected discovery during import to be rejected, got %v", err) + } + + close(fetcher.release) + waitForImportRunState(t, service, run.Name, webv1.ImportRunState_IMPORT_RUN_STATE_SUCCEEDED) +} + +func TestImportLogsRejectsInvalidRequests(t *testing.T) { + service, workspaceName := newImportTestService(t, &fakeFetcher{}) + + if _, err := service.ImportLogs(context.Background(), connect.NewRequest(&webv1.ImportLogsRequest{ + Parent: workspaceName, + })); connect.CodeOf(err) != connect.CodeInvalidArgument { + t.Fatalf("expected missing source to be rejected, got %v", err) + } + + if _, err := service.ImportLogs(context.Background(), connect.NewRequest(&webv1.ImportLogsRequest{ + Parent: workspaceName, + Source: &webv1.ImportLogsRequest_GcpLogging{ + GcpLogging: &webv1.GcpLoggingSource{ProjectId: " "}, + }, + })); connect.CodeOf(err) != connect.CodeInvalidArgument { + t.Fatalf("expected missing project to be rejected, got %v", err) + } +} + +func TestNewWorkspaceServiceMarksInterruptedImportRunsFailed(t *testing.T) { + root := t.TempDir() + workspaceDir := filepath.Join(root, "gcp-import") + if err := os.MkdirAll(filepath.Join(workspaceDir, "logs"), 0o755); err != nil { + t.Fatal(err) + } + record := workspace.ImportRunRecord{ + ID: "run-1", + State: workspace.ImportRunStateRunning, + Source: workspace.ImportSourceGCPLogging, + StartedAt: time.Now().UTC(), + } + if err := workspace.WriteImportRunRecord(workspaceDir, record); err != nil { + t.Fatal(err) + } + + if _, err := NewWorkspaceService(ServiceConfig{Root: root, GcpFetcher: &fakeFetcher{}}); err != nil { + t.Fatalf("NewWorkspaceService: %v", err) + } + + got, err := workspace.ReadImportRunRecord(workspaceDir, "run-1") + if err != nil { + t.Fatal(err) + } + if got.State != workspace.ImportRunStateFailed { + t.Fatalf("state = %s, want FAILED", got.State) + } + if got.Error == nil || got.Error.Code != "IMPORT_INTERRUPTED" { + t.Fatalf("unexpected error: %+v", got.Error) + } +} diff --git a/pkg/webapp/service.go b/pkg/webapp/service.go index c3ad56d..f5d4ecc 100644 --- a/pkg/webapp/service.go +++ b/pkg/webapp/service.go @@ -17,6 +17,7 @@ import ( "github.com/google/uuid" webv1 "github.com/strrl/lapp/gen/go/lapp/web/v1" "github.com/strrl/lapp/gen/go/lapp/web/v1/webv1connect" + "github.com/strrl/lapp/pkg/gcplog" "github.com/strrl/lapp/pkg/workspace" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -27,12 +28,13 @@ var workspaceIDPattern = regexp.MustCompile(`[^a-z0-9]+`) var errorLikePattern = regexp.MustCompile(`(?i)(error|warn|fatal|panic|exception|failed|timeout)`) type WorkspaceService struct { - root string - apiKey string - model string - mu sync.Mutex - running map[string]bool - labeler workspace.PatternLabeler + root string + apiKey string + model string + mu sync.Mutex + running map[string]bool + labeler workspace.PatternLabeler + gcpFetcher gcplog.Fetcher } type ServiceConfig struct { @@ -40,6 +42,9 @@ type ServiceConfig struct { APIKey string Model string Labeler workspace.PatternLabeler + // GcpFetcher fetches logs from Google Cloud Logging for ImportLogs. + // Nil means the real Cloud Logging admin API client. + GcpFetcher gcplog.Fetcher } func NewWorkspaceService(config ServiceConfig) (*WorkspaceService, error) { @@ -51,16 +56,24 @@ func NewWorkspaceService(config ServiceConfig) (*WorkspaceService, error) { } root = filepath.Join(home, ".lapp", "workspaces") } + fetcher := config.GcpFetcher + if fetcher == nil { + fetcher = gcplog.NewFetcher() + } service := &WorkspaceService{ - root: root, - apiKey: config.APIKey, - model: config.Model, - running: make(map[string]bool), - labeler: config.Labeler, + root: root, + apiKey: config.APIKey, + model: config.Model, + running: make(map[string]bool), + labeler: config.Labeler, + gcpFetcher: fetcher, } if err := service.recoverInterruptedDiscoveryRuns(); err != nil { return nil, err } + if err := service.recoverInterruptedImportRuns(); err != nil { + return nil, err + } return service, nil } @@ -230,7 +243,7 @@ func (s *WorkspaceService) CreateDiscoveryRun(_ context.Context, req *connect.Re } runID, uuidErr := uuid.NewV7() if uuidErr != nil { - s.setRunning(id, false) + s.clearRunning(id) return nil, connect.NewError(connect.CodeInternal, uuidErr) } record := workspace.DiscoveryRunRecord{ @@ -243,11 +256,11 @@ func (s *WorkspaceService) CreateDiscoveryRun(_ context.Context, req *connect.Re StartedAt: time.Now().UTC(), } if err := workspace.WriteDiscoveryRunRecord(s.workspaceDir(id), record); err != nil { - s.setRunning(id, false) + s.clearRunning(id) return nil, connect.NewError(connect.CodeInternal, err) } go func() { - defer s.setRunning(id, false) + defer s.clearRunning(id) slog.Info("DiscoveryRun started", "workspace", id, "run", runID.String()) result, err := workspace.Discover(context.Background(), workspace.DiscoveryConfig{ Dir: s.workspaceDir(id), @@ -568,13 +581,9 @@ func (s *WorkspaceService) reserveDiscovery(id string) bool { return true } -func (s *WorkspaceService) setRunning(id string, running bool) { +func (s *WorkspaceService) clearRunning(id string) { s.mu.Lock() defer s.mu.Unlock() - if running { - s.running[id] = true - return - } delete(s.running, id) } diff --git a/pkg/workspace/importrun.go b/pkg/workspace/importrun.go new file mode 100644 index 0000000..d38fb89 --- /dev/null +++ b/pkg/workspace/importrun.go @@ -0,0 +1,126 @@ +package workspace + +import ( + "encoding/json" + "os" + "path/filepath" + "time" + + "github.com/go-errors/errors" +) + +const ImportRunsDirName = "import-runs" + +// ImportSourceGCPLogging identifies imports fetched from Google Cloud Logging. +const ImportSourceGCPLogging = "gcp-logging" + +type ImportRunState string + +const ( + ImportRunStateQueued ImportRunState = "QUEUED" + ImportRunStateRunning ImportRunState = "RUNNING" + ImportRunStateSucceeded ImportRunState = "SUCCEEDED" + ImportRunStateFailed ImportRunState = "FAILED" +) + +// ImportRunRecord persists the state of one log import job. On success the +// fetched entries land as a regular file in the workspace logs directory. +type ImportRunRecord struct { + ID string `json:"id"` + State ImportRunState `json:"state"` + Source string `json:"source"` + GCPProject string `json:"gcp_project,omitempty"` + Filter string `json:"filter,omitempty"` + Progress *ImportRunProgress `json:"progress,omitempty"` + Error *ImportRunError `json:"error,omitempty"` + StartedAt time.Time `json:"started_at"` + FinishedAt *time.Time `json:"finished_at,omitempty"` + EntryCount int `json:"entry_count"` + LogFileName string `json:"log_file_name,omitempty"` +} + +type ImportRunProgress struct { + FetchedCount int `json:"fetched_count"` +} + +type ImportRunError struct { + Code string `json:"code"` + Message string `json:"message"` +} + +func ImportRunsDir(workspaceDir string) string { + return filepath.Join(workspaceDir, ImportRunsDirName) +} + +func ImportRunDir(workspaceDir, runID string) string { + return filepath.Join(ImportRunsDir(workspaceDir), runID) +} + +func ImportRunRecordPath(workspaceDir, runID string) string { + return filepath.Join(ImportRunDir(workspaceDir, runID), "run.json") +} + +func WriteImportRunRecord(workspaceDir string, record ImportRunRecord) error { + if record.ID == "" { + return errors.New("import run id is required") + } + runDir := ImportRunDir(workspaceDir, record.ID) + if err := os.MkdirAll(runDir, 0o755); err != nil { + return errors.Errorf("create import run dir: %w", err) + } + data, err := json.MarshalIndent(record, "", " ") + if err != nil { + return errors.Errorf("marshal import run: %w", err) + } + tmp, err := os.CreateTemp(runDir, "run-*.json.tmp") + if err != nil { + return errors.Errorf("create temp import run: %w", err) + } + tmpPath := tmp.Name() + defer func() { _ = os.Remove(tmpPath) }() + if _, err := tmp.Write(append(data, '\n')); err != nil { + _ = tmp.Close() + return errors.Errorf("write temp import run: %w", err) + } + if err := tmp.Close(); err != nil { + return errors.Errorf("close temp import run: %w", err) + } + if err := os.Rename(tmpPath, ImportRunRecordPath(workspaceDir, record.ID)); err != nil { + return errors.Errorf("write import run: %w", err) + } + return nil +} + +func ReadImportRunRecord(workspaceDir, runID string) (ImportRunRecord, error) { + data, err := os.ReadFile(ImportRunRecordPath(workspaceDir, runID)) + if err != nil { + return ImportRunRecord{}, errors.Errorf("read import run: %w", err) + } + var record ImportRunRecord + if err := json.Unmarshal(data, &record); err != nil { + return ImportRunRecord{}, errors.Errorf("decode import run: %w", err) + } + return record, nil +} + +func ListImportRunRecords(workspaceDir string) ([]ImportRunRecord, error) { + entries, err := os.ReadDir(ImportRunsDir(workspaceDir)) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, errors.Errorf("read import runs dir: %w", err) + } + records := make([]ImportRunRecord, 0, len(entries)) + for _, entry := range entries { + if !entry.IsDir() { + continue + } + record, err := ReadImportRunRecord(workspaceDir, entry.Name()) + if err != nil { + return nil, err + } + records = append(records, record) + } + return records, nil +} diff --git a/pkg/workspace/importrun_test.go b/pkg/workspace/importrun_test.go new file mode 100644 index 0000000..7730a51 --- /dev/null +++ b/pkg/workspace/importrun_test.go @@ -0,0 +1,80 @@ +package workspace + +import ( + "testing" + "time" +) + +func TestImportRunRecordRoundTrip(t *testing.T) { + dir := t.TempDir() + finishedAt := time.Date(2026, 7, 1, 10, 5, 0, 0, time.UTC) + record := ImportRunRecord{ + ID: "run-1", + State: ImportRunStateSucceeded, + Source: ImportSourceGCPLogging, + GCPProject: "my-project", + Filter: "severity>=ERROR", + Progress: &ImportRunProgress{ + FetchedCount: 1200, + }, + StartedAt: time.Date(2026, 7, 1, 10, 0, 0, 0, time.UTC), + FinishedAt: &finishedAt, + EntryCount: 1200, + LogFileName: "gcp-my-project-1.log", + } + + if err := WriteImportRunRecord(dir, record); err != nil { + t.Fatal(err) + } + got, err := ReadImportRunRecord(dir, "run-1") + if err != nil { + t.Fatal(err) + } + if got.ID != record.ID || got.State != record.State || got.Source != record.Source { + t.Errorf("got %+v, want %+v", got, record) + } + if got.EntryCount != 1200 || got.LogFileName != record.LogFileName { + t.Errorf("got %+v, want %+v", got, record) + } + if got.Progress == nil || got.Progress.FetchedCount != 1200 { + t.Errorf("progress = %+v, want fetched 1200", got.Progress) + } +} + +func TestWriteImportRunRecordRequiresID(t *testing.T) { + if err := WriteImportRunRecord(t.TempDir(), ImportRunRecord{}); err == nil { + t.Fatal("expected error for missing id") + } +} + +func TestListImportRunRecords(t *testing.T) { + dir := t.TempDir() + + records, err := ListImportRunRecords(dir) + if err != nil { + t.Fatal(err) + } + if len(records) != 0 { + t.Errorf("records = %d, want 0", len(records)) + } + + for _, id := range []string{"run-1", "run-2"} { + record := ImportRunRecord{ + ID: id, + State: ImportRunStateRunning, + Source: ImportSourceGCPLogging, + StartedAt: time.Now().UTC(), + } + if err := WriteImportRunRecord(dir, record); err != nil { + t.Fatal(err) + } + } + + records, err = ListImportRunRecords(dir) + if err != nil { + t.Fatal(err) + } + if len(records) != 2 { + t.Errorf("records = %d, want 2", len(records)) + } +} diff --git a/proto/lapp/web/v1/web.proto b/proto/lapp/web/v1/web.proto index 3b8c663..87bd918 100644 --- a/proto/lapp/web/v1/web.proto +++ b/proto/lapp/web/v1/web.proto @@ -20,6 +20,10 @@ service WorkspaceService { rpc GetDiscoveryRun(GetDiscoveryRunRequest) returns (GetDiscoveryRunResponse); rpc ListDiscoveryRuns(ListDiscoveryRunsRequest) returns (ListDiscoveryRunsResponse); + rpc ImportLogs(ImportLogsRequest) returns (ImportLogsResponse); + rpc GetImportRun(GetImportRunRequest) returns (GetImportRunResponse); + rpc ListImportRuns(ListImportRunsRequest) returns (ListImportRunsResponse); + rpc ListPatterns(ListPatternsRequest) returns (ListPatternsResponse); rpc GetPattern(GetPatternRequest) returns (GetPatternResponse); rpc GetErrorsView(GetErrorsViewRequest) returns (GetErrorsViewResponse); @@ -104,6 +108,44 @@ enum DiscoveryStep { DISCOVERY_STEP_WRITING_RESULTS = 5; } +message ImportRun { + string name = 1; + string import_run_id = 2; + ImportRunState state = 3; + string source = 4; + google.protobuf.Timestamp started_at = 5; + google.protobuf.Timestamp finished_at = 6; + int32 entry_count = 7; + string log_file_name = 8; + ImportRunProgress progress = 9; + ImportRunError error = 10; +} + +enum ImportRunState { + IMPORT_RUN_STATE_UNSPECIFIED = 0; + IMPORT_RUN_STATE_QUEUED = 1; + IMPORT_RUN_STATE_RUNNING = 2; + IMPORT_RUN_STATE_SUCCEEDED = 3; + IMPORT_RUN_STATE_FAILED = 4; +} + +message ImportRunProgress { + int32 fetched_count = 1; +} + +message ImportRunError { + string code = 1; + string message = 2; +} + +message GcpLoggingSource { + string project_id = 1; + string filter = 2; + google.protobuf.Timestamp start_time = 3; + google.protobuf.Timestamp end_time = 4; + int32 limit = 5; +} + message Pattern { string name = 1; string pattern_id = 2; @@ -209,6 +251,33 @@ message ListDiscoveryRunsResponse { repeated DiscoveryRun discovery_runs = 1; } +message ImportLogsRequest { + string parent = 1; + oneof source { + GcpLoggingSource gcp_logging = 2; + } +} + +message ImportLogsResponse { + ImportRun import_run = 1; +} + +message GetImportRunRequest { + string name = 1; +} + +message GetImportRunResponse { + ImportRun import_run = 1; +} + +message ListImportRunsRequest { + string parent = 1; +} + +message ListImportRunsResponse { + repeated ImportRun import_runs = 1; +} + message ListPatternsRequest { string parent = 1; }